Пакет java.lang.invoke
Пакет java.lang.invoke предоставляет низкоуровневые примитивы для взаимодействия с виртуальной машиной Java.
Как описано в спецификации виртуальной машины Java, некоторым типам в этом пакете предоставляется специальное обращение со стороны виртуальной машины:
- Классы
MethodHandleVarHandleсодержат полиморфные по сигнатуре методы, которые могут быть связаны независимо от их описания типа. Обычно для связи метода требуется точное соответствие описаний типов. - Формат байткода JVM поддерживает непосредственные константы классов
MethodHandleиMethodType. - Инструкция
invokedynamicиспользует загрузочныеMethodHandleконстанты для динамического разрешенияCallSiteобъектов для пользовательского поведения вызова методов. - Инструкция
ldcиспользует загрузочныеMethodHandleконстанты для динамического разрешения пользовательских константных значений.
Динамическое разрешение сайтов вызовов и констант
Следующая низкоуровневая информация обобщает соответствующие части спецификации виртуальной машины Java. Для получения полной информации, пожалуйста, обратитесь к текущей версии этой спецификации.Динамически вычисляемые сайты вызовов
Инструкцияinvokedynamic изначально находится в несвязанном состоянии. В этом состоянии нет целевого метода для вызова инструкцией. Прежде чем JVM сможет выполнить инструкцию invokedynamic, инструкция должна быть предварительно связана. Связывание выполняется путем вызова загрузочного метода, которому предоставляется статическая информация о вызове, и который должен произвести CallSite, определяющий поведение вызова.
Каждая инструкция invokedynamic статически определяет свой собственный загрузочный метод как ссылку на пул констант. Ссылка на пул констант также определяет имя вызова и описание типа метода, аналогично invokestatic и другим инструкциям вызова.
Динамически вычисляемые константы
Пул констант может содержать константы, помеченныеCONSTANT_Dynamic, оснащенные загрузочными методами, которые выполняют их разрешение. Такая динамическая константа изначально находится в состоянии ожидания разрешения. Перед использованием динамически вычисляемой константы JVM должна сначала её разрешить. Разрешение динамически вычисляемых констант выполняется путем вызова загрузочного метода, которому предоставляется статическая информация о константе, и который должен произвести значение константы её статически объявленного типа. Каждая динамически вычисляемая константа статически определяет свой собственный загрузочный метод как ссылку на пул констант. Ссылка на пул констант также определяет имя константы и описание типа поля, аналогично getstatic и другим инструкциям ссылки на поля. (Грубо говоря, динамически вычисляемая константа — к динамически вычисляемому месту вызова, как CONSTANT_Fieldref — к CONSTANT_Methodref.)
Выполнение загрузочных методов
Разрешение динамически вычисляемого места вызова или константы начинается с разрешения констант из пула констант для следующих элементов:- загрузочный метод,
CONSTANT_MethodHandle - типы
ClassилиMethodType, полученные из компонента типа описанияCONSTANT_NameAndType - статические аргументы, если они есть (обратите внимание, что статические аргументы сами могут быть динамически вычисляемыми константами)
Загрузочный метод затем вызывается, как если бы он был вызван с помощью MethodHandle.invoke, со следующими аргументами:
MethodHandles.Lookup, который является объектом поиска в классе-отправителе, в котором находится динамически вычисляемая константа или место вызоваString, имя, упомянутое вCONSTANT_NameAndTypeMethodTypeилиClass, разрешенное описание типаCONSTANT_NameAndTypeClass, разрешенное описание типа константы, если это динамическая константа- дополнительные разрешенные статические аргументы, если они есть
Для динамически вычисляемого места вызова возвращаемый результат должен быть непустой ссылкой на CallSite. Тип целевого места вызова должен точно совпадать с типом, полученным из описания типа вызова и переданным загрузочному методу. Если эти условия не выполняются, выбрасывается BootstrapMethodError. При успехе место вызова затем навсегда связывается с инструкцией invokedynamic.
Для динамически вычисляемой константы первый параметр загрузочного метода должен быть приводимым к типу MethodHandles.Lookup. Если это условие не выполняется, выбрасывается BootstrapMethodError. При успехе результат загрузочного метода кэшируется как разрешенное значение константы.
Если во время выполнения загрузочного метода произойдет исключение, например, E, то разрешение завершается аномально. E повторно выбрасывается, если тип E является Error или его подклассом, в противном случае выбрасывается BootstrapMethodError, который обертывает E. В этом случае та же ошибка будет выбрасываться при всех последующих попытках выполнить инструкцию invokedynamic или загрузить динамически вычисляемую константу.
Время разрешения
Инструкцияinvokedynamic связывается непосредственно перед её первым выполнением. Динамически вычисляемая константа разрешается непосредственно перед первым её использованием (путем помещения её в стек или связывания её как параметра загрузочного метода). Вызов загрузочного метода, реализующего связывание, происходит в потоке, который пытается выполнить первое выполнение или первое использование. Если таких потоков несколько, то загрузочный метод может вызываться в нескольких потоках одновременно. Поэтому загрузочные методы, которые обращаются к глобальным данным приложения, должны принимать обычные меры предосторожности против гонок. В любом случае, каждая инструкция invokedynamic либо несвязана, либо связана с уникальным объектом CallSite.
В приложении, которое требует инструкций invokedynamic с индивидуально изменяемым поведением, их загрузочные методы должны генерировать отдельные объекты CallSite, по одному для каждого запроса связывания. В качестве альтернативы приложение может связать один объект CallSite с несколькими инструкциями invokedynamic , в этом случае изменение целевого метода станет видимым в каждой из инструкций.
Если несколько потоков одновременно выполняют загрузочный метод для одной динамически вычисляемой точки вызова или константы, JVM должна выбрать один результат загрузочного метода и установить его видимым для всех потоков. Любые другие вызовы загрузочных методов разрешаются к выполнению, но их результаты игнорируются.
Обсуждение: Эти правила не позволяют JVM совместно использовать места вызова или вызывать загрузочные методы «без причины». Каждая инструкция invokedynamic переходит не более чем один раз из несвязанного состояния в связанное, непосредственно перед своим первым вызовом. Нет возможности отменить эффект завершенного вызова загрузочного метода.
Типы загрузочных методов
Для динамически вычисляемого места вызова загрузочный метод вызывается с параметрами типамиMethodHandles.Lookup, String, MethodType, и типами любых статических аргументов; возвращаемый тип — CallSite. Для динамически вычисляемой константы загрузочный метод вызывается с параметрами типами MethodHandles.Lookup, String, Class, и типами любых статических аргументов; возвращаемый тип — тип, представленный Class.
Поскольку MethodHandle.invoke допускает адаптации между типом вызываемого метода и типом метода обработчика загрузочного метода, существует гибкость в объявлении загрузочного метода. Для динамически вычисляемой константы первый тип параметра загрузочного метода должен быть приводим к типу MethodHandles.Lookup, помимо этого ограничения, та же степень гибкости применяется к загрузочным методам динамически вычисляемых точек вызова и динамически вычисляемых констант. Примечание: это ограничение позволяет в будущем возможности вызова загрузочного метода только с типами параметров статических аргументов, тем самым поддерживая более широкий спектр методов, совместимых со статическими аргументами (такие как методы, которые не объявляют или не требуют параметров метаданных поиска, имени и типа).
Например, для динамически вычисляемого места вызова первым аргументом может быть Object вместо MethodHandles.Lookup, а возвращаемый тип также может быть Object вместо CallSite. (Обратите внимание, что типы и количество аргументов стека ограничивают допустимые типы загрузочных методов подходящими статическими методами и конструкторами.)
Если введённое значение является примитивным типом, оно может быть преобразовано в ссылку путём преобразования боксёрного типа. Если загрузочный метод является методом с переменным числом аргументов (его модификатор 0x0080 установлен), то некоторые или все указанные здесь аргументы могут быть собраны в параметр массива с последующим аргументом. (Это не специальное правило, а скорее полезное следствие взаимодействия между CONSTANT_MethodHandle константами, модификатором метода с переменным числом аргументов и преобразованием asVarargsCollector.)
Учитывая эти правила, вот примеры допустимых объявлений загрузочного метода для динамически вычисляемых мест вызова при различных количествах N дополнительных аргументов. Первая строка (отмеченная *) будет работать для любого количества дополнительных аргументов.
| N | Образец загрузочного метода |
|---|---|
| * |
|
| 0 |
|
| 1 | CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
|
| 2 |
|
String и Integer (или int) соответственно. Предпоследний пример предполагает, что все дополнительные аргументы имеют тип String. Другие примеры работают со всеми типами дополнительных аргументов. Обратите внимание, что все примеры, кроме второго и третьего, также работают с динамически вычисляемыми константами, если тип возвращаемого значения изменён для совместимости с объявленным типом константы (например, Object, который всегда совместим). Поскольку динамически вычисляемые константы могут быть предоставлены в качестве статических аргументов методам инициализации, нет ограничений на типы аргументов инициализации. Однако аргументы типа boolean, byte, short, или char не могут быть непосредственно предоставлены записями пула констант CONSTANT_Integer, так как преобразования asType не выполняют необходимые преобразования сужающих примитивов.
В приведенных выше примерах тип возвращаемого значения всегда CallSite, но это не обязательное свойство методов инициализации. В случае вызова динамически вычисляемого места вызова единственное требование состоит в том, что тип возвращаемого значения метода инициализации должен быть преобразуем (используя преобразования asType ) к CallSite, что означает, что тип возвращаемого значения метода инициализации может быть Object или ConstantCallSite. В случае динамически разрешенной константы тип возвращаемого значения метода инициализации должен быть преобразуем к типу константы, представленному её описателем типа поля. Например, если описатель типа поля динамической константы "C" (char) , то тип возвращаемого значения метода инициализации может быть Object, Character, или char, но не int или Integer.
- С момента:
- 1.7
| Интерфейс | Описание |
|---|---|
| MethodHandleInfo | Символьная ссылка, полученная путем разбиения прямого метода обработки на составляющие символьные части. |
| Класс | Описание |
|---|---|
| CallSite |
|
| ConstantBootstraps | Методы инициализации для динамически вычисляемых констант. |
| ConstantCallSite |
|
| LambdaMetafactory | Методы для облегчения создания простых «функциональных объектов», которые реализуют один или несколько интерфейсов путём делегирования предоставленному |
| MethodHandle | Обработка метода — это типизированная, непосредственно исполняемая ссылка на базовый метод, конструктор, поле или аналогичную низкоуровневую операцию с необязательными преобразованиями аргументов или возвращаемых значений. |
| MethodHandleProxies | Этот класс состоит исключительно из статических методов, которые помогают адаптировать обработчики методов к другим типам JVM, таким как интерфейсы. |
| MethodHandles | Этот класс состоит исключительно из статических методов, которые работают с обработчиками методов или возвращают их. |
| MethodHandles.Lookup | Объект поиска — это фабрика для создания обработчиков методов, когда для создания требуется проверка доступа. |
| MethodType | Тип метода представляет аргументы и тип возвращаемого значения, принимаемые и возвращаемые обработчиком метода, или аргументы и тип возвращаемого значения, передаваемые и ожидаемые вызывающим обработчиком метода. |
| MutableCallSite |
|
| SerializedLambda | Сериализованная форма лямбда-выражения. |
| StringConcatFactory | Методы для облегчения создания методов конкатенации строк, которые могут использоваться для эффективной конкатенации известного количества аргументов известных типов, возможно после адаптации типов и частичной оценки аргументов. |
| SwitchPoint | |
| VarHandle | VarHandle — это динамически строго типизированная ссылка на переменную или на параметрически определённую семью переменных, включая статические поля, нестатические поля, элементы массива или компоненты вне heap-структуры данных. |
| VolatileCallSite |
|
| Перечисление | Описание |
|---|---|
| VarHandle.AccessMode | Набор режимов доступа, которые указывают, как осуществляется доступ к переменной, на которую ссылается VarHandle. |
| Исключение | Описание |
|---|---|
| LambdaConversionException | LambdaConversionException |
| StringConcatException | Исключение StringConcatException выбрасывается |
| WrongMethodTypeException | Выбрасывается, чтобы указать, что код попытался вызвать обработчик метода с неправильным типом метода. |
© 1993, 2020, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html