VM (выполнение JavaScript)¶
Стабильность: 2 – Стабильная
АПИ является удовлетворительным. Совместимость с NPM имеет высший приоритет и не будет нарушена кроме случаев явной необходимости.
Модуль node:vm позволяет компилировать и выполнять код в контекстах виртуальной машины V8.
Модуль node:vm не является механизмом безопасности. Не используйте его для запуска недоверенного кода.
Код на JavaScript можно скомпилировать и выполнить сразу или скомпилировать, сохранить и выполнить позже.
Типичный сценарий — выполнить код в другом контексте V8. Тогда у вызываемого кода другой глобальный объект, чем у вызывающего.
Контекст можно задать, контекстируя объект. Вызываемый код воспринимает любое свойство контекста как глобальную переменную. Изменения глобальных переменных, внесённые вызываемым кодом, отражаются в объекте контекста.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Класс: vm.Script¶
Экземпляры класса vm.Script содержат предварительно скомпилированные сценарии, которые можно выполнять в заданных контекстах.
new vm.Script(code[, options])¶
Добавлено в: v0.3.1
code<string>JavaScript-код для компиляции.options<Object>|<string>filename<string>Имя файла в трассировках стека, создаваемых этим сценарием. По умолчанию:'evalmachine.<anonymous>'.lineOffset<number>Смещение номера строки в трассировках стека. По умолчанию:0.columnOffset<number>Смещение номера колонки первой строки в трассировках стека. По умолчанию:0.cachedData<Buffer>|<TypedArray>|<DataView>НеобязательныйBuffer,TypedArrayилиDataViewс данными кэша кода V8 для исходного текста. Если задано, свойствоcachedDataRejectedбудетtrueилиfalseв зависимости от того, принял ли V8 данные.produceCachedData<boolean>Приtrueи отсутствииcachedDataV8 попытается создать данные кэша кода дляcode. При успехе будет созданBufferс кэшем кода V8 и сохранён в свойствеcachedDataвозвращённого экземпляраvm.Script. СвойствоcachedDataProducedбудетtrueилиfalseв зависимости от успеха. Эта опция устарела; используйтеscript.createCachedData(). По умолчанию:false.importModuleDynamicallyFunction | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER Задаёт, как загружать модули при вычислении этого сценария при вызовеimport(). Опция относится к экспериментальному API модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.
Если options — строка, она задаёт имя файла.
Создание объекта vm.Script компилирует code, но не выполняет его. Скомпилированный vm.Script можно многократно выполнять позже. Код code не привязан к глобальному объекту; привязка выполняется перед каждым запуском, только на время этого запуска.
script.cachedDataRejected¶
- Тип:
<boolean>| undefined
Если при создании vm.Script передан cachedData, это значение будет true или false в зависимости от того, принял ли V8 данные. Иначе значение — undefined.
script.createCachedData()¶
- Возвращает:
<Buffer>
Создаёт кэш кода для опции cachedData конструктора Script. Возвращает Buffer. Метод можно вызывать в любое время и любое число раз.
Кэш кода Script не содержит наблюдаемых из JavaScript состояний. Его можно сохранять рядом с исходным текстом и использовать для создания новых экземпляров Script многократно.
Функции в исходнике Script могут быть помечены как лениво компилируемые и не компилируются при создании Script. Они скомпилируются при первом вызове. Кэш кода сериализует метаданные, которые V8 уже знает о Script, чтобы ускорить дальнейшие компиляции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
script.runInContext(contextifiedObject[, options])¶
Добавлено в: v0.3.1
contextifiedObject<Object>Контекстированный объект, возвращённый методомvm.createContext().options<Object>displayErrors<boolean>Приtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию:true.timeout<integer>Число миллисекунд выполненияcodeдо принудительной остановки. При остановке выбрасываетсяError. Значение — строго положительное целое.breakOnSigint<boolean>ПриtrueсигналSIGINT(Ctrl+C) прерывает выполнение и выбрасываетError. Обработчики, подключённые черезprocess.on('SIGINT'), на время выполнения сценария отключаются, затем снова действуют. По умолчанию:false.- Возвращает:
<any>результат последнего выполненного оператора сценария.
Выполняет скомпилированный код объекта vm.Script в заданном contextifiedObject и возвращает результат. У выполняемого кода нет доступа к локальной области видимости.
Пример компилирует код, увеличивающий глобальную переменную, задаёт другую глобальную переменную и несколько раз выполняет сценарий. Глобальные переменные хранятся в объекте context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Использование опций timeout или breakOnSigint запускает новые циклы событий и связанные потоки, что даёт ненулевые накладные расходы.
script.runInNewContext([contextObject[, options]])¶
Добавлено в: 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(Ctrl+C) прерывает выполнение и выбрасываетError. Обработчикиprocess.on('SIGINT')отключаются на время выполнения скрипта, затем снова работают. По умолчанию:false.contextName<string>Читаемое имя нового контекста. По умолчанию:'VM Context i', гдеi— возрастающий индекс созданного контекста.contextOrigin<string>Origin нового контекста для отображения. Формат как у URL, но только схема, хост и при необходимости порт, как уurl.originуURL. Обратите внимание: без завершающего слэша, он означает путь. По умолчанию:''.contextCodeGeneration<Object>microtaskMode<string>ЕслиafterEvaluate, микрозадачи (черезPromiseиasync function) выполняются сразу после скрипта. В этом случае они входят в ограниченияtimeoutиbreakOnSigint.- Возвращает:
<any>результат последнего выполненного в скрипте выражения.
Эквивалентно script.runInContext(vm.createContext(options), options). Делает следующее:
- Создаёт новый контекст.
- Если
contextObject— объект, контекстифицирует его в новом контексте. ЕслиcontextObject—undefined, создаётся новый объект и контекстифицируется. Еслиvm.constants.DONT_CONTEXTIFY— ничего не контекстифицировать. - Запускает скомпилированный код
vm.Scriptв созданном контексте. Код не видит область, из которой вызван метод. - Возвращает результат.
В примере ниже компилируется код, задающий глобальную переменную, затем он выполняется несколько раз в разных контекстах. Глобальные переменные изолированы в каждом context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
script.runInThisContext([options])¶
Добавлено в: v0.3.1
options<Object>displayErrors<boolean>Еслиtrue, приErrorпри компиляцииcodeстрока с ошибочным кодом добавляется к трассировке стека. По умолчанию:true.timeout<integer>Число миллисекунд выполненияcodeдо остановки. При остановке выбрасываетсяError. Значение — строго положительное целое.breakOnSigint<boolean>Еслиtrue, сигналSIGINT(Ctrl+C) прерывает выполнение и выбрасываетError. Обработчикиprocess.on('SIGINT')отключаются на время выполнения сценария, затем снова работают. По умолчанию:false.- Возвращает:
<any>результат последнего выполненного в сценарии оператора.
Выполняет скомпилированный код объекта vm.Script в контексте текущего объекта global. У выполняемого кода нет доступа к локальной области видимости, но есть к текущему global.
В примере ниже код увеличивает глобальную переменную и выполняется несколько раз:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
script.sourceMapURL¶
- Тип:
<string>| undefined
Если сценарий скомпилирован из исходника с магическим комментарием source map, это свойство будет содержать URL карты исходников.
1 2 3 4 5 6 7 8 9 | |
1 2 3 4 5 6 7 8 9 | |
Класс: vm.Module¶
Стабильность: 1 - Экспериментальная
Эта возможность доступна только при включённом флаге командной строки --experimental-vm-modules.
Класс vm.Module даёт низкоуровневый интерфейс для работы с модулями ECMAScript в контекстах VM. Это аналог класса vm.Script, который тесно следует записям Module Record в спецификации ECMAScript.
В отличие от vm.Script, каждый объект vm.Module при создании привязан к контексту.
Работа с объектом vm.Module состоит из трёх шагов: создание/разбор, связывание и вычисление. Эти шаги показаны в примере ниже.
Реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Взаимодействовать с загрузчиком пока нельзя, хотя поддержка планируется.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
module.error¶
- Тип:
<any>
Если module.status равен 'errored', это свойство содержит исключение, выброшенное модулем при вычислении. При любом другом статусе обращение к свойству приводит к выброшенному исключению.
Значение undefined нельзя использовать для случая «исключения не было» из‑за возможной неоднозначности с throw undefined;.
Соответствует полю [[EvaluationError]] у Cyclic Module Record в спецификации ECMAScript.
module.evaluate([options])¶
options<Object>timeout<integer>Задаёт число миллисекунд выполнения до принудительного завершения. Если выполнение будет прервано, будет выброшенError. Это значение должно быть строго положительным целым числом.breakOnSigint<boolean>ПриtrueсигналSIGINT(Ctrl+C) прерывает выполнение и выбрасываетError. Уже подключённые обработчики черезprocess.on('SIGINT')на время выполнения сценария отключаются, затем снова работают. По умолчанию:false.- Возвращает:
<Promise>при успехе выполняется сundefined.
Вычисляет модуль и его зависимости. Соответствует полю Evaluate() concrete method у Cyclic Module Record в спецификации ECMAScript.
Если модуль — vm.SourceTextModule, evaluate() нужно вызывать после инстанцирования модуля; иначе evaluate() вернёт отклонённый промис.
Для vm.SourceTextModule промис, возвращаемый evaluate(), может быть выполнен синхронно или асинхронно:
- Если у
vm.SourceTextModuleнет top-levelawaitни в нём самом, ни в зависимостях, промис будет выполнен синхронно после вычисления модуля и всех зависимостей. - При успешном вычислении промис синхронно разрешается в
undefined. - Если вычисление завершается исключением, промис синхронно отклоняется этим исключением (то же, что в
module.error). - Если у
vm.SourceTextModuleесть top-levelawaitв нём или в зависимостях, промис будет выполнен асинхронно после вычисления модуля и всех зависимостей. - При успешном вычислении промис асинхронно разрешается в
undefined. - Если вычисление завершается исключением, промис асинхронно отклоняется этим исключением.
Если модуль — vm.SyntheticModule, evaluate() всегда возвращает промис, который выполняется синхронно; см. Evaluate() of a Synthetic Module Record:
- Если
evaluateCallback, переданный в конструктор, синхронно выбрасывает исключение,evaluate()возвращает промис, который будет синхронно отклонён этим исключением. - Если
evaluateCallbackне выбрасывает исключение,evaluate()возвращает промис, который будет синхронно разрешён вundefined.
evaluateCallback у vm.SyntheticModule выполняется синхронно внутри вызова evaluate(), его возвращаемое значение отбрасывается. Поэтому если evaluateCallback — асинхронная функция, промис, возвращаемый evaluate(), не отразит её асинхронное поведение, а отклонения от асинхронного evaluateCallback будут потеряны.
evaluate() можно вызвать снова после того, как модуль уже вычислен:
- Если первое вычисление завершилось успешно (
module.status—'evaluated'), ничего не делает и возвращает промис, разрешающийся вundefined. - Если первое вычисление завершилось исключением (
module.status—'errored'), снова отклоняется тем исключением, что было при первом вычислении.
Этот метод нельзя вызывать, пока модуль вычисляется (module.status — 'evaluating').
module.identifier¶
- Тип:
<string>
Идентификатор текущего модуля, заданный в конструкторе.
module.link(linker)¶
linker<Function>-
specifier<string>Спецификатор запрошенного модуля:1 2
import foo from 'foo'; // ^^^^^ the module specifier -
referencingModule<vm.Module>ОбъектModule, для которого вызываетсяlink(). -
extra<Object> -
Возвращает:
<vm.Module>|<Promise> - Возвращает:
<Promise>
Связывает зависимости модуля. Метод нужно вызвать до вычисления, не более одного раза на модуль.
Используйте sourceTextModule.linkRequests(modules) и sourceTextModule.instantiate(), чтобы связывать модули синхронно или асинхронно.
Функция должна вернуть объект Module или Promise, который в итоге разрешается в Module. Возвращённый Module должен удовлетворять двум инвариантам:
- Принадлежит тому же контексту, что и родительский
Module. - Его
statusне должен быть'errored'.
Если у возвращённого Module status равен 'unlinked', этот метод будет рекурсивно вызван для возвращённого Module с той же функцией linker.
link() возвращает Promise, который разрешится, когда все связывания дадут корректный Module, или будет отклонён, если функция-связчик выбросит исключение или вернёт некорректный Module.
Функция-связчик примерно соответствует реализации абстрактной операции HostResolveImportedModule в спецификации ECMAScript, с отличиями:
- Функция-связчик может быть асинхронной, тогда как HostResolveImportedModule — синхронна.
Фактическая реализация HostResolveImportedModule, используемая при связывании модулей, возвращает модули, связанные в процессе связывания. К этому моменту все модули уже полностью связаны, поэтому реализация HostResolveImportedModule по спецификации полностью синхронна.
Соответствует полю Link() concrete method у Cyclic Module Record в спецификации ECMAScript.
module.namespace¶
- Тип:
<Object>
Объект пространства имён модуля. Доступен только после завершения связывания (вызова module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status¶
- Тип:
<string>
Текущий статус модуля. Одно из значений:
-
'unlinked':module.link()ещё не вызывался. -
'linking':module.link()вызван, но ещё не разрешены все промисы, возвращённые функцией-связчиком. -
'linked': модуль успешно связан, все зависимости связаны, ноmodule.evaluate()ещё не вызывался. -
'evaluating': модуль вычисляется черезmodule.evaluate()у него самого или родительского модуля. -
'evaluated': модуль успешно вычислен. -
'errored': модуль был вычислен, но было выброшено исключение.
Кроме 'errored', строка статуса соответствует полю [[Status]] у Cyclic Module Record в спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]], отличным от undefined.
Класс: vm.SourceTextModule¶
Стабильность: 1 - Экспериментальная
Эта возможность доступна только при включённом флаге командной строки --experimental-vm-modules.
- Наследует:
<vm.Module>
Класс vm.SourceTextModule реализует Source Text Module Record, как определено в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])¶
code<string>код модуля JavaScript для разбораoptionsidentifier<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 модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.
Создаёт новый экземпляр SourceTextModule.
Свойства import.meta, значениями которых являются объекты, могут позволить модулю обращаться к данным вне указанного context. Используйте vm.runInContext(), чтобы создавать объекты в нужном контексте.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
sourceTextModule.createCachedData()¶
- Возвращает:
<Buffer>
Создаёт кэш кода для опции cachedData конструктора SourceTextModule. Возвращает Buffer. Метод можно вызывать любое число раз до вычисления модуля.
Кэш кода SourceTextModule не содержит наблюдаемых из JavaScript состояний. Его можно сохранять рядом с исходным текстом и многократно использовать для создания новых экземпляров SourceTextModule.
Функции в исходнике SourceTextModule могут быть помечены как лениво компилируемые и не компилируются при создании SourceTextModule. Они скомпилируются при первом вызове. Кэш кода сериализует метаданные, которые V8 уже знает о SourceTextModule, чтобы ускорить дальнейшие компиляции.
1 2 3 4 5 6 7 8 | |
sourceTextModule.dependencySpecifiers¶
Стабильность: 0 - Устарело
Вместо этого используйте sourceTextModule.moduleRequests.
- Тип:
<string[]>
Спецификаторы всех зависимостей этого модуля. Возвращаемый массив заморожен, изменять его нельзя.
Соответствует полю [[RequestedModules]] у Cyclic Module Record в спецификации ECMAScript.
sourceTextModule.hasAsyncGraph()¶
- Возвращает:
<boolean>
Обходит граф зависимостей и возвращает true, если в зависимостях или в самом модуле есть выражения top-level await, иначе возвращает false.
Поиск может быть медленным при достаточно большом графе.
Требуется предварительно инстанцировать модуль. Если модуль ещё не инстанцирован, будет выброшена ошибка.
sourceTextModule.hasTopLevelAwait()¶
- Возвращает:
<boolean>
Возвращает, содержит ли сам модуль выражения top-level await.
Соответствует полю [[HasTLA]] у Cyclic Module Record в спецификации ECMAScript.
sourceTextModule.instantiate()¶
- Возвращает:
<undefined>
Инстанцирует модуль со связанными запрошенными модулями.
Разрешает импортированные привязки модуля, в том числе при реэкспорте имён. Если какие-то привязки разрешить нельзя, ошибка выбрасывается синхронно.
Если среди запрошенных модулей есть циклические зависимости, метод sourceTextModule.linkRequests(modules) нужно вызвать для всех модулей в цикле до вызова этого метода.
sourceTextModule.linkRequests(modules)¶
modules<vm.Module[]>Массив объектовvm.Module, от которых зависит этот модуль. Порядок модулей в массиве совпадает с порядкомsourceTextModule.moduleRequests.- Возвращает:
<undefined>
Связывает зависимости модуля. Метод нужно вызвать до вычисления, не более одного раза на модуль.
Порядок экземпляров в массиве modules должен соответствовать порядку разрешения sourceTextModule.moduleRequests. Если два запроса модуля имеют одинаковый спецификатор и атрибуты импорта, они должны разрешаться в один и тот же экземпляр модуля, иначе будет выброшен ERR_MODULE_LINK_MISMATCH. Например, при связывании запросов для этого модуля:
1 2 | |
Массив modules должен содержать две ссылки на один и тот же экземпляр, потому что оба запроса модуля совпадают, но в двух фазах.
Если у модуля нет зависимостей, массив modules может быть пустым.
sourceTextModule.moduleRequests можно использовать для реализации определяемой хостом абстрактной операции HostLoadImportedModule в спецификации ECMAScript, а sourceTextModule.linkRequests() — чтобы вызвать определённую в спецификации FinishLoadingImportedModule для модуля со всеми зависимостями одним пакетом.
Синхронность или асинхронность разрешения зависимостей определяет создатель SourceTextModule.
После связывания каждого модуля из массива modules вызовите sourceTextModule.instantiate().
sourceTextModule.moduleRequests¶
- Тип:
<ModuleRequest[]>Зависимости этого модуля.
Запрошенные импортные зависимости этого модуля. Возвращаемый массив заморожен, изменять его нельзя.
Например, для исходного текста:
1 2 3 4 5 | |
значение sourceTextModule.moduleRequests будет таким:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
Класс: vm.SyntheticModule¶
Стабильность: 1 - Экспериментальная
Эта возможность доступна только при включённом флаге командной строки --experimental-vm-modules.
- Наследует:
<vm.Module>
Класс vm.SyntheticModule реализует Synthetic Module Record, как определено в спецификации WebIDL. Синтетические модули дают универсальный интерфейс для подключения к графам модулей ECMAScript источников, не являющихся JavaScript.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
new vm.SyntheticModule(exportNames, evaluateCallback[, options])¶
exportNames<string[]>Массив имён, которые будут экспортироваться из модуля.evaluateCallback<Function>Вызывается при вычислении модуля.optionsidentifier<string>Строка для трассировок стека. По умолчанию:'vm:module(i)', гдеi— возрастающий индекс, зависящий от контекста.context<Object>Контекстифицированный объект, возвращённыйvm.createContext(), в котором компилируется и вычисляется этотModule.
Создаёт новый экземпляр SyntheticModule.
Объекты, присваиваемые экспортам этого экземпляра, могут позволить импортёрам модуля обращаться к данным вне указанного context. Используйте vm.runInContext(), чтобы создавать объекты в нужном контексте.
syntheticModule.setExport(name, value)¶
Метод задаёт ячейки привязок экспорта модуля указанным значением.
1 2 3 4 5 6 7 8 9 | |
1 2 3 4 5 6 7 8 | |
Тип: ModuleRequest¶
- Тип:
<Object> specifier<string>Спецификатор запрошенного модуля.attributes<Object>Значение"with", переданное в WithClause в ImportDeclaration, или пустой объект, если значение не задано.phase<string>Фаза запрошенного модуля ("source"или"evaluation").
ModuleRequest описывает запрос на импорт модуля с заданными атрибутами импорта и фазой.
vm.compileFunction(code[, params[, options]])¶
Добавлено в: 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[]>Массив расширений контекста (объектов, оборачивающих текущую область видимости), применяемых при компиляции. По умолчанию:[].importModuleDynamicallyFunction | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER Задаёт, как загружать модули при вычислении этой функции при вызовеimport(). Опция относится к экспериментальному API модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.- Возвращает:
<Function>
Компилирует переданный код в указанном контексте (если контекст не задан, используется текущий) и возвращает функцию с заданными params.
vm.constants¶
- Тип:
<Object>
Возвращает объект с наиболее используемыми константами для операций VM.
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER¶
Стабильность: 1.1 - Активная разработка
Константа для опции importModuleDynamically у vm.Script и vm.compileFunction(): Node.js использует стандартный загрузчик ESM из основного контекста для загрузки запрошенного модуля.
Подробнее см. Поддержка динамического import() в API компиляции.
vm.createContext([contextObject[, options]])¶
Добавлено в: 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>Origin нового контекста для отображения. Формат как у URL, но только схема, хост и при необходимости порт, как у свойстваurl.originуURL. Завершающий слэш не указывайте — он означает путь. По умолчанию:''.codeGeneration<Object>microtaskMode<string>ПриafterEvaluateмикрозадачи (черезPromiseиasync function) выполняются сразу после сценария вscript.runInContext(). В этом случае они входят в действие опцийtimeoutиbreakOnSigint.importModuleDynamicallyFunction | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER Задаёт, как загружать модули при вызовеimport()в этом контексте без сценария-реферера или модуля. Опция относится к экспериментальному API модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.- Возвращает:
<Object>контекстифицированный объект.
Если передан объект contextObject, метод vm.createContext() подготавливает этот объект и возвращает ссылку для использования в вызовах vm.runInContext() или script.runInContext(). В таких сценариях глобальный объект оборачивается contextObject, сохраняя все имеющиеся свойства и добавляя встроенные объекты и функции, как у обычного глобального объекта. Вне сценариев, запускаемых модулем vm, глобальные переменные среды выполнения не меняются.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Если contextObject опущен (или явно передан как undefined), возвращается новый пустой контекстифицированный объект.
Когда глобальный объект в новом контекте контекстифицирован, у него есть особенности по сравнению с обычными глобальными объектами: например, его нельзя заморозить. Чтобы создать контекст без этих особенностей, передайте vm.constants.DONT_CONTEXTIFY в качестве аргумента contextObject. Подробнее — в описании vm.constants.DONT_CONTEXTIFY.
Метод vm.createContext() удобен для создания одного контекста, в котором выполняется несколько сценариев. Например, при эмуляции браузера можно создать один контекст глобального объекта окна и выполнять в нём все теги <script>.
Заданные name и origin контекста видны через API инспектора.
vm.isContext(object)¶
Возвращает true, если переданный object был контекстифицирован через vm.createContext(), или если это глобальный объект контекста, созданного с vm.constants.DONT_CONTEXTIFY.
vm.measureMemory([options])¶
Стабильность: 1 - Экспериментальная
Измеряет память, известную V8 и используемую всеми контекстами текущего изолята V8, либо основным контекстом.
options<Object>Необязательно.mode<string>'summary'или'detailed'. В режиме summary возвращается только память основного контекста. В режиме detailed — память для всех контекстов, известных текущему изоляту V8. По умолчанию:'summary'execution<string>'default'или'eager'. При значении по умолчанию промис не разрешится, пока не начнётся следующая запланированная сборка мусора (это может занять время или не произойти, если процесс завершится раньше). Приeagerсборка мусора запускается сразу для измерения памяти. По умолчанию:'default'- Возвращает:
<Promise>при успешном измерении промис разрешается объектом с информацией об использовании памяти. Иначе отклоняется с ошибкойERR_CONTEXT_NOT_INITIALIZED.
Формат объекта, с которым может разрешиться промис, зависит от движка V8 и может меняться между версиями V8.
Результат отличается от статистики v8.getHeapSpaceStatistics(): vm.measureMemory() измеряет память, достижимую из каждого контекста V8 в текущем экземпляре движка, тогда как v8.getHeapSpaceStatistics() измеряет память, занятую каждым пространством кучи в текущем экземпляре V8.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
vm.runInContext(code, contextifiedObject[, options])¶
Добавлено в: v0.3.1
code<string>Код JavaScript для компиляции и выполнения.contextifiedObject<Object>Контекстифицированный объект, который будет использоваться какglobalпри компиляции и выполненииcode.options<Object>|<string>filename<string>Имя файла в трассировках стека для этого сценария. По умолчанию:'evalmachine.<anonymous>'.lineOffset<number>Смещение номера строки в трассировках стека. По умолчанию:0.columnOffset<number>Смещение номера колонки первой строки в трассировках стека. По умолчанию:0.displayErrors<boolean>Приtrue, если при компиляцииcodeвозникаетError, строка с ошибочным кодом добавляется к трассировке стека. По умолчанию:true.timeout<integer>Задаёт число миллисекунд выполненияcodeдо принудительного завершения. Если выполнение будет остановлено, будет выброшенError. Это значение должно быть строго положительным целым числом.breakOnSigint<boolean>ПриtrueсигналSIGINT(Ctrl+C) прерывает выполнение и выбрасываетError. Уже подключённые обработчики черезprocess.on('SIGINT')на время выполнения сценария отключаются, затем снова работают. По умолчанию:false.cachedData<Buffer>|<TypedArray>|<DataView>НеобязательныйBuffer,TypedArrayилиDataViewс данными кэша кода V8 для переданного исходника.importModuleDynamicallyFunction | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER Задаёт, как загружать модули при вычислении этого сценария при вызовеimport(). Опция относится к экспериментальному API модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.
Метод vm.runInContext() компилирует code, выполняет его в контексте contextifiedObject и возвращает результат. У выполняемого кода нет доступа к локальной области видимости. Объект contextifiedObject должен быть заранее контекстифицирован через vm.createContext().
Если options — строка, она задаёт имя файла.
В следующем примере компилируются и выполняются разные сценарии в одном контекстифицированном объекте:
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 | |
vm.runInNewContext(code[, contextObject[, options]])¶
Добавлено в: v0.3.1
code<string>Код JavaScript для компиляции и выполнения.contextObject<Object>|<vm.constants.DONT_CONTEXTIFY>| undefined Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстифицирован. Еслиundefined, для обратной совместимости создаётся пустой контекстифицированный объект.options<Object>|<string>filename<string>Имя файла в трассировках стека для этого сценария. По умолчанию:'evalmachine.<anonymous>'.lineOffset<number>Смещение номера строки в трассировках стека. По умолчанию:0.columnOffset<number>Смещение номера колонки первой строки в трассировках стека. По умолчанию:0.displayErrors<boolean>Приtrue, если при компиляцииcodeвозникаетError, строка с ошибочным кодом добавляется к трассировке стека. По умолчанию:true.timeout<integer>Задаёт число миллисекунд выполненияcodeдо принудительного завершения. Если выполнение будет остановлено, будет выброшенError. Это значение должно быть строго положительным целым числом.breakOnSigint<boolean>ПриtrueсигналSIGINT(Ctrl+C) прерывает выполнение и выбрасываетError. Уже подключённые обработчики черезprocess.on('SIGINT')на время выполнения сценария отключаются, затем снова работают. По умолчанию:false.contextName<string>Читаемое имя создаваемого контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста.contextOrigin<string>Origin нового контекста для отображения. Формат как у URL, но только схема, хост и при необходимости порт, как уurl.originуURL. Завершающий слэш не указывайте — он означает путь. По умолчанию:''.contextCodeGeneration<Object>cachedData<Buffer>|<TypedArray>|<DataView>НеобязательныйBuffer,TypedArrayилиDataViewс данными кэша кода V8 для переданного исходника.importModuleDynamicallyFunction | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER Задаёт, как загружать модули при вычислении этого сценария при вызовеimport(). Опция относится к экспериментальному API модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.microtaskMode<string>ПриafterEvaluateмикрозадачи (черезPromiseиasync function) выполняются сразу после сценария. В этом случае они входят в действие опцийtimeoutиbreakOnSigint.- Возвращает:
<any>результат последнего выполненного в сценарии оператора.
Сокращение для (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Если options — строка, она задаёт имя файла.
Метод делает следующее:
- Создаёт новый контекст.
- Если
contextObject— объект, контекстифицирует его в новом контексте. ЕслиcontextObject—undefined, создаётся новый объект и контекстифицируется. ЕслиcontextObject—vm.constants.DONT_CONTEXTIFY, ничего не контекстифицировать. - Компилирует код как
vm.Script. - Выполняет скомпилированный код в созданном контексте. Код не видит область видимости, из которой вызван метод.
- Возвращает результат.
В следующем примере компилируется и выполняется код, увеличивающий глобальную переменную и задающий новую. Эти глобальные переменные лежат в contextObject.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
vm.runInThisContext(code[, options])¶
Добавлено в: v0.3.1
code<string>Код JavaScript для компиляции и выполнения.options<Object>|<string>filename<string>Имя файла в трассировках стека для этого сценария. По умолчанию:'evalmachine.<anonymous>'.lineOffset<number>Смещение номера строки в трассировках стека. По умолчанию:0.columnOffset<number>Смещение номера колонки первой строки в трассировках стека. По умолчанию:0.displayErrors<boolean>Приtrue, если при компиляцииcodeвозникаетError, строка с ошибочным кодом добавляется к трассировке стека. По умолчанию:true.timeout<integer>Задаёт число миллисекунд выполненияcodeдо принудительного завершения. Если выполнение будет остановлено, будет выброшенError. Это значение должно быть строго положительным целым числом.breakOnSigint<boolean>ПриtrueсигналSIGINT(Ctrl+C) прерывает выполнение и выбрасываетError. Уже подключённые обработчики черезprocess.on('SIGINT')на время выполнения сценария отключаются, затем снова работают. По умолчанию:false.cachedData<Buffer>|<TypedArray>|<DataView>НеобязательныйBuffer,TypedArrayилиDataViewс данными кэша кода V8 для переданного исходника.importModuleDynamicallyFunction | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER Задаёт, как загружать модули при вычислении этого сценария при вызовеimport(). Опция относится к экспериментальному API модулей. В production не рекомендуется. Подробнее см. Поддержка динамическогоimport()в API компиляции.- Возвращает:
<any>результат последнего выполненного в сценарии оператора.
vm.runInThisContext() компилирует code, выполняет его в контексте текущего global и возвращает результат. У выполняемого кода нет доступа к локальной области видимости, но есть к текущему объекту global.
Если options — строка, она задаёт имя файла.
Следующий пример показывает использование и vm.runInThisContext(), и встроенной eval() для одного и того же кода:
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 | |
Так как у vm.runInThisContext() нет доступа к локальной области видимости, localVar не меняется. Прямой вызов eval(), наоборот, имеет доступ к локальной области, поэтому localVar меняется. В этом смысле vm.runInThisContext() близок к косвенному вызову eval(), например (0,eval)('code').
Пример: HTTP-сервер внутри VM¶
Используя script.runInThisContext() или vm.runInThisContext(), код выполняется в текущем глобальном контексте V8. Переданный в VM код имеет свою изолированную область видимости.
Чтобы запустить простой веб-сервер на модуле node:http, переданный в контекст код должен сам вызвать require('node:http') или получить ссылку на node:http снаружи. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
require() в этом примере разделяет состояние с контекстом, из которого он передан. Это может быть рискованно при выполнении недоверенного кода, например при нежелательном изменении объектов в контексте.
Что значит «контекстифицировать» объект?¶
Весь JavaScript в Node.js выполняется в области «контекста». Согласно V8 Embedder's Guide:
В V8 контекст — это среда выполнения, в которой отдельные независимые приложения на JavaScript могут работать в одном экземпляре V8. Нужно явно указать контекст, в котором должен выполняться код на JavaScript.
При вызове vm.createContext() с объектом аргумент contextObject используется для обёртки глобального объекта нового экземпляра V8 Context (если contextObject — undefined, перед контекстификацией из текущего контекста создаётся новый объект). Этот V8 Context даёт коду, выполняемому через методы модуля node:vm, изолированную глобальную среду. Создание V8 Context и связывание его с contextObject во внешнем контексте в этом документе называется «контекстификацией» объекта.
Контекстификация вносит особенности в значение globalThis в контексте: например, его нельзя заморозить, и оно не совпадает по ссылке с contextObject во внешнем контексте.
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
Чтобы получить контекст с обычным глобальным объектом и прокси глобального объекта во внешнем контексте с меньшим числом особенностей, укажите vm.constants.DONT_CONTEXTIFY как аргумент contextObject.
vm.constants.DONT_CONTEXTIFY¶
Эта константа в качестве аргумента contextObject в API vm указывает Node.js создать контекст без оборачивания глобального объекта дополнительной обёрткой в специфичной для Node.js манере. В результате globalThis в новом контексте ведёт себя ближе к обычному глобальному объекту.
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 | |
Если vm.constants.DONT_CONTEXTIFY передан в vm.createContext() как contextObject, возвращаемый объект похож на прокси к глобальному объекту нового контекста с меньшим числом особенностей Node.js. Он совпадает по ссылке с globalThis в новом контексте, его можно менять извне и через него обращаться к встроенным объектам нового контекста.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Взаимодействие таймаута с асинхронными задачами и Promise¶
Promise и async function могут планировать задачи, выполняемые движком JavaScript асинхронно. По умолчанию эти задачи выполняются после того, как завершатся все функции на текущем стеке. Так можно «выйти» из действия опций timeout и breakOnSigint.
Например, код ниже, выполняемый через vm.runInNewContext() с таймаутом 5 миллисекунд, планирует бесконечный цикл после разрешения промиса. Запланированный цикл таймаутом не прерывается:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Это можно исправить, передав microtaskMode: 'afterEvaluate' при создании Context:
1 2 3 4 5 6 7 8 9 10 11 | |
1 2 3 4 5 6 7 8 9 10 11 | |
В этом случае микрозадача, запланированная через promise.then(), выполнится до возврата из vm.runInNewContext() и может быть прервана механизмом timeout. Это относится только к коду в vm.Context, например vm.runInThisContext() эту опцию не принимает.
Колбэки Promise попадают в очередь микрозадач того контекста, в котором они были созданы. Если в примере выше заменить () => loop() на просто loop, то loop попадёт в глобальную очередь микрозадач, потому что это функция из внешнего (основного) контекста, и таймаут её тоже не ограничит.
Если внутри vm.Context доступны process.nextTick(), queueMicrotask(), setTimeout(), setImmediate() и т.п., переданные в них функции попадают в глобальные очереди, общие для всех контекстов. Поэтому такие колбэки тоже нельзя надёжно ограничить таймаутом.
Когда microtaskMode равен 'afterEvaluate': осторожно с общими Promise между контекстами¶
В режиме 'afterEvaluate' у Context своя очередь микрозадач, отдельная от глобальной очереди внешнего (основного) контекста. Этот режим нужен, чтобы действовал timeout и breakOnSigint при асинхронных задачах, но он усложняет совместное использование промисов между контекстами.
В примере ниже промис создаётся во внутреннем контексте и передаётся во внешний. Когда внешний контекст делает await по промису, порядок выполнения ломается неожиданным образом: console.log не выполняется.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Чтобы безопасно делить промисы между контекстами с разными очередями микрозадач, нужно гарантировать, что задачи во внутренней очереди микрозадач будут выполняться всякий раз, когда внешний контекст ставит задачу во внутреннюю очередь микрозадач.
Задачи в очереди микрозадач контекста выполняются при каждом вызове runInContext() или SourceTextModule.evaluate() для сценария или модуля в этом контексте. В нашем примере нормальный ход выполнения можно восстановить, запланировав второй вызов runInContext() до await inner_promise.
1 2 3 4 5 6 7 8 9 | |
Примечание: строго говоря, в этом режиме node:vm расходится с буквой спецификации ECMAScript по постановке задач в очередь: асинхронные задачи из разных контекстов могут выполняться в ином порядке, чем они были поставлены в очередь.
Поддержка динамического import() в API компиляции¶
Следующие API поддерживают опцию importModuleDynamically для динамического import() в коде, скомпилированном модулем vm.
new vm.Scriptvm.compileFunction()new vm.SourceTextModulevm.runInThisContext()vm.runInContext()vm.runInNewContext()vm.createContext()
Опция по-прежнему относится к экспериментальному API модулей. В production не рекомендуется.
Когда опция importModuleDynamically не задана или равна undefined¶
Если опция не указана или равна undefined, код с import() всё ещё можно скомпилировать через API vm, но при выполнении скомпилированного кода, когда фактически вызывается import(), результат будет отклонён с ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.
Когда importModuleDynamically равен vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER¶
Сейчас эта опция не поддерживается для vm.SourceTextModule.
При этой опции, когда в скомпилированном коде инициируется import(), Node.js использует стандартный загрузчик ESM из основного контекста для загрузки запрошенного модуля и возвращает его выполняемому коду.
Так компилируемый код получает доступ к встроенным модулям Node.js вроде fs или http. Если код выполняется в другом контексте, учтите, что объекты, созданные модулями, загруженными из основного контекста, принадлежат основному контексту и не являются instanceof встроенных классов в новом контексте.
1 2 3 4 5 6 7 8 | |
1 2 3 4 5 6 7 8 9 | |
Эта опция также позволяет сценарию или функции загружать пользовательские модули:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
При загрузке пользовательских модулей через стандартный загрузчик из основного контекста учтите следующее:
- Разрешение модуля выполняется относительно опции
filename, переданной вvm.Scriptилиvm.compileFunction(). Поддерживаетсяfilenameкак абсолютный путь, так и строка URL. Еслиfilename— строка, которая не является ни абсолютным путём, ни URL, или если онаundefined, разрешение идёт относительно текущего рабочего каталога процесса. Дляvm.createContext()разрешение всегда относительно текущего рабочего каталога, так как опция используется только при отсутствии сценария-реферера или модуля. - Для одного и того же
filename, разрешающегося в конкретный путь, после первой успешной загрузки модуля с этого пути результат может кэшироваться, и повторная загрузка с того же пути вернёт то же самое. Еслиfilename— строка URL, кэш не совпадёт при разных query-параметрах. Дляfilename, не являющихся строками URL, сейчас нельзя обойти кэширование.
Когда importModuleDynamically — функция¶
Если importModuleDynamically — функция, она вызывается при import() в скомпилированном коде, чтобы задать способ компиляции и вычисления запрошенного модуля. Сейчас процесс Node.js должен быть запущен с флагом --experimental-vm-modules, иначе опция не сработает. Если флаг не задан, этот колбэк игнорируется. Если выполняемый код фактически вызывает import(), результат будет отклонён с ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.
Колбэк importModuleDynamically(specifier, referrer, importAttributes) имеет сигнатуру:
specifier<string>спецификатор, переданный вimport()referrer<vm.Script>|<Function>|<vm.SourceTextModule>|<Object>Реферер — скомпилированныйvm.Scriptдляnew vm.Script,vm.runInThisContext,vm.runInContextиvm.runInNewContext; скомпилированныйFunctionдляvm.compileFunction; скомпилированныйvm.SourceTextModuleдляnew vm.SourceTextModule; объект контекстаObjectдляvm.createContext().importAttributes<Object>Значение"with", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не задано.phase<string>Фаза динамического импорта ("source"или"evaluation").- Возвращает:
<Module Namespace Object>|<vm.Module>Рекомендуется возвращатьvm.Module, чтобы использовать отслеживание ошибок и избежать проблем с пространствами имён, где среди экспортов есть функцияthen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |