Пакет java.lang.invoke
java.lang.invoke предоставляет примитивы низкого уровня для взаимодействия с виртуальной машиной Java. Как описано в спецификации виртуальной машины Java, определённые типы в этом пакете получают специальное обращение от виртуальной машины:
- Классы
MethodHandleиVarHandleсодержат полиморфные по сигнатуре методы, которые могут быть связаны независимо от их описателя типа. Обычно связывание метода требует точного совпадения описателей типов. - Формат байткода 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, со следующими аргументами:
- объект поиска, который является объектом поиска в классе вызывающего, в котором происходит динамически вычисляемая константа или сайт вызова
- имя, упомянутое в
CONSTANT_NameAndType - описатель типа
MethodTypeилиClass, разрешенный описатель типаCONSTANT_NameAndType - разрешенный описатель типа константы, если это динамическая константа
- дополнительные разрешенные статические аргументы, если таковые имеются
Для динамически вычисляемого сайта вызова возвращаемое значение должно быть ссылкой на не-null объект 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.
- Since:
- 1.7
| Класс | Описание |
|---|---|
| CallSite | |
| ConstantBootstraps | Методы загрузки для динамически вычисляемых констант. |
| ConstantCallSite | ConstantCallSite — это CallSite, целевой объект которого является постоянным и никогда не может быть изменён. |
| LambdaConversionException | LambdaConversionException |
| LambdaMetafactory | Методы для облегчения создания простых "объектов функций", которые реализуют один или несколько интерфейсов путём делегирования предоставленному MethodHandle, возможно после адаптации типов и частичной оценки аргументов. |
| MethodHandle | Обработчик метода — это типизированная, непосредственно исполняемая ссылка на базовую функцию, конструктор, поле или аналогичную низкоуровневую операцию, с необязательными преобразованиями аргументов или возвращаемых значений. |
| MethodHandleInfo | Символическая ссылка, полученная путём разбиения прямого обработчика метода на составляющие символические части. |
| MethodHandleProxies | Этот класс состоит исключительно из статических методов, которые помогают адаптировать обработчики методов к другим типам JVM, таким как интерфейсы. |
| MethodHandles | Этот класс состоит исключительно из статических методов, которые работают с обработчиками методов или возвращают их. |
| MethodHandles.Lookup | Объект поиска — это фабрика для создания обработчиков методов, когда создание требует проверки доступа. |
| MethodHandles.Lookup.ClassOption | Набор опций класса, определяющих, добавляется ли скрытый класс, созданный методом Lookup::defineHiddenClass, динамически в качестве нового члена к вложенному классу поиска и/или имеет ли скрытый класс сильную связь с загрузчиком классов, помеченным как его определяющий загрузчик. |
| MethodType | Тип метода представляет аргументы и возвращаемый тип, принимаемые и возвращаемые обработчиком метода, или аргументы и возвращаемый тип, передаваемые и ожидаемые вызывающей стороной обработчика метода. |
| MutableCallSite | MutableCallSite — это CallSite, переменная целевого объекта которого ведёт себя как обычное поле. |
| SerializedLambda | Сериализованная форма лямбда-выражения. |
| StringConcatException | StringConcatException выбрасывается StringConcatFactory при нарушении связующих инвариантов. |
| StringConcatFactory | Методы для облегчения создания методов конкатенации строк, которые могут использоваться для эффективной конкатенации известного числа аргументов известных типов, возможно после адаптации типов и частичной оценки аргументов. |
| SwitchPoint | SwitchPoint — это объект, который может публиковать переходы состояния другим потокам. |
| TypeDescriptor | Сущность, имеющая описатель типа. |
| TypeDescriptor.OfField<F extends TypeDescriptor.OfField<F>> | Сущность, имеющая описатель типа поля. |
|
TypeDescriptor.OfMethod<F extends TypeDescriptor.OfField<F>, |
Сущность, имеющая описатель типа метода. Описатели методов, соответствующие JVMS 4.3.3, могут быть описаны номинально с помощью MethodType::describeConstable; в противном случае они не могут быть описаны номинально. |
| VarHandle | VarHandle — это динамически строго типизированная ссылка на переменную или на параметрически определённую семью переменных, включая статические поля, нестатические поля, элементы массивов или компоненты вне-кучной структуры данных. |
| VarHandle.AccessMode | Набор режимов доступа, которые определяют способ доступа к переменной, на которую ссылается VarHandle. |
| VarHandle.VarHandleDesc | Номинальный описатель для константы VarHandle. |
| VolatileCallSite | VolatileCallSite — это CallSite, целевой объект которого ведёт себя как переменная volatile. |
| WrongMethodTypeException | Выбрасывается для указания на то, что код попытался вызвать обработчик метода с неправильным типом метода. |
© 1993, 2021, 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/17/docs/api/java.base/java/lang/invoke/package-summary.html