Модуль jdk.dynalink
Dynalink — это библиотека для динамической привязки операций высокого уровня к объектам. Эти операции включают "чтение свойства", "запись свойства", "вызов функции" и так далее. Dynalink особенно полезен для реализации языков программирования, где по крайней мере некоторые выражения имеют динамические типы (то есть типы, которые нельзя определить статически), а операции над динамическими типами выражаются как узлы вызова. Эти узлы вызова будут привязаны к соответствующим целевым обработчикам методов во время выполнения в зависимости от фактических типов значений, к которым были вычислены выражения. Они могут изменяться между вызовами, что требует многократной перепривязки узла вызова для адаптации к новым типам; Dynalink обрабатывает всё это и многое другое.
Dynalink поддерживает реализацию языков программирования с моделями объектов, которые отличаются (даже радикально) от модели JVM, основанной на классах, и имеют свои пользовательские преобразования типов.
Dynalink тесно связан с пакетом java.lang.invoke и опирается на него.
Хотя java.lang.invoke предоставляет API низкого уровня для динамической привязки invokedynamic узлов вызова, он не предоставляет способ выразить операции высокого уровня над объектами, ни методы, которые их реализуют. Эти операции — обычные в объектно-ориентированных средах: доступ к свойствам, доступ к элементам коллекций, вызов методов и конструкторов (возможно, с множественным диспетчерированием, например, эквиваленты связывания и выполнения в Java для разрешения перегруженных методов). Все это функции, которые обычно желательны в языке на JVM. Если язык статически типизирован, и его система типов соответствует системе типов JVM, он может этого достичь с помощью обычных инструкций вызова, доступа к полям и т. д. (например, invokevirtual, getfield). Однако, если язык динамический (следовательно, типы некоторых выражений неизвестны до вычисления во время выполнения), или его модель объектов или система типов не близки к модели JVM, то он должен использовать invokedynamic узлы вызова вместо этого и позволить Dynalink управлять ими.
Пример
Dynalink, вероятно, лучше всего объясняется примером, демонстрирующим его использование. Предположим, у вас есть программа на языке, где вам не нужно объявлять тип объекта, и вы хотите получить доступ к свойству в нём:var color = obj.color;Если вы сгенерировали класс Java для представления вышеприведенной однострочной программы, его байт-код будет выглядеть примерно так:
aload 2 // load "obj" on stack invokedynamic "GET:PROPERTY:color"(Object)Object // invoke property getter on object of unknown type astore 3 // store the return value into local variable "color"Для привязки
invokedynamic инструкции нам нужен метод запуска. Минимальный метод запуска с Dynalink может выглядеть так:
import java.lang.invoke.*;
import jdk.dynalink.*;
import jdk.dynalink.support.*;
class MyLanguageRuntime {
private static final DynamicLinker dynamicLinker = new DynamicLinkerFactory().createLinker();
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) {
return dynamicLinker.link(
new SimpleRelinkableCallSite(
new CallSiteDescriptor(lookup, parseOperation(name), type)));
}
private static Operation parseOperation(String name) {
...
}
}
Несколько важных объектов в приведенном фрагменте кода: -
DynamicLinker— это основной объект в Dynalink, он координирует привязку узлов вызова к обработчикам методов, которые реализуют названные в них операции. Он настраивается и создаётся с помощьюDynamicLinkerFactory. - При вызове метода запуска, ему необходимо создать объект
CallSite. В Dynalink эти узлы вызова должны дополнительно реализовывать интерфейсRelinkableCallSite. "Перепривязываемый" здесь указывает на то, что если узел вызова встречает объекты разных типов во время выполнения, его целевой объект будет изменён на обработчик метода, который может выполнить операцию с только что встреченным типом.SimpleRelinkableCallSiteиChainedCallSite(не используются в приведённом примере) — две реализации, уже предоставленные библиотекой. - Dynalink использует объекты
CallSiteDescriptor, чтобы сохранить параметры метода запуска: поиск и тип метода, так как ему они понадобятся всякий раз, когда потребуется перепривязать узел вызова. - Dynalink использует объекты
Operation, чтобы выразить динамические операции. Он не предписывает, как вы будете кодировать операции в своём узле вызова. Вот почему в примере функцияparseOperationоставлена пустой, и ожидается, что вы предоставите код для разбора строки"GET:PROPERTY:color"в имени узла вызова в объект операции получения именованного свойства какStandardOperation.GET.withNamespace(StandardNamespace.PROPERTY).named("color").
Что вы уже можете сделать с вышеуказанной настройкой? DynamicLinkerFactory по умолчанию создаёт DynamicLinker , который может связывать Java-объекты с обычной Java-семантикой. Если у вас есть эти три простых класса:
public class A {
public String color;
public A(String color) { this.color = color; }
}
public class B {
private String color;
public B(String color) { this.color = color; }
public String getColor() { return color; }
}
public class C {
private int color;
public C(int color) { this.color = color; }
public int getColor() { return color; }
}
и вы каким-то образом создаёте их экземпляры и передаёте их в свой узел вызова на вашем языке программирования:
for each(var obj in [new A("red"), new B("green"), new C(0x0000ff)]) {
print(obj.color);
}
, то при первом вызове Dynalink свяжет операцию получения .color с операцией получения поля для A.color, при втором вызове он перепривяжет её к B.getColor() , возвращающей String, а в конечном итоге при третьем вызове он перепривяжет её к C.getColor() , возвращающей int. SimpleRelinkableCallSite , который мы использовали выше, запоминает только привязку для последнего встреченного типа (он реализует то, что известно как мономорфный кэш встраивания). Другая уже предоставленная реализация, ChainedCallSite будет запоминать привязки для нескольких различных типов (это полиморфный кэш встраивания) и, вероятно, является лучшим выбором в серьёзных приложениях. Dynalink и создание байт-кода
CallSite объекты обычно создаются как часть запуска invokedynamic инструкций в байт-коде. Следовательно, Dynalink обычно используется в рамках языковых сред выполнения, которые компилируют программы в Java .class байт-код. Dynalink не затрагивает аспекты создания классов байт-кода или их загрузки в JVM. Тем не менее, Dynalink также может использоваться без компиляции байт-кода (например, в интерпретаторах языков) путём явного создания CallSite объектов и их связывания с представлениями динамических операций в интерпретируемой программе (например, типичным представлением были бы некоторые объекты узлов в дереве синтаксического разбора). Доступные операции
Dynalink определяет несколько стандартных операций в своём классеStandardOperation. Привязка для Java-объектов может привязать все эти операции, и вам рекомендуется по крайней мере поддерживать и использовать эти операции в вашем языке тоже. Стандартные операции GET и SET должны быть объединены по крайней мере с одним Namespace, чтобы быть полезными, например, чтобы выразить операцию получения свойства, вы бы использовали StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY). Dynalink определяет три стандартных пространства имен в классе StandardNamespace. Для связывания фиксированного имени с операцией вы можете использовать NamedOperation, как в предыдущем примере: StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY).named("color") выражает операцию получения свойства с именем "цвет". Операции с несколькими пространствами имён
Некоторые языки могут не иметь отдельных пространств имён на объектах для свойств, элементов и методов, а конструкция исходного языка может одновременно обращаться к нескольким из них. Dynalink поддерживает указание несколькихNamespace объектов с помощью NamespaceOperation. Языкоспецифические привязки
Языки, которые определяют свою собственную модель объектов, отличную от модели JVM, основанной на классах, и/или используют свои собственные преобразования типов, должны создать свои языкоспецифические привязки. См. пакетjdk.dynalink.linker и, в частности, интерфейс GuardingDynamicLinker для начала. Dynalink и Java-объекты
DynamicLinker объекты, создаваемые DynamicLinkerFactory по умолчанию, содержат внутренний экземпляр BeansLinker, который является языкоспецифической привязкой, реализующей обычную Java-семантику для всех вышеупомянутых операций и способной привязать любой Java-объект, который не удалось привязать никакой другой языкоспецифической привязке. Таким образом, все языковые среды выполнения имеют встроенную совместимость с обычными Java-объектами. Подробности о том, как он связывает различные операции, см. в BeansLinker. Взаимодействие между языками
DynamicLinkerFactory может быть настроен с помощью загрузчика классов. Он будет пытаться создать все GuardingDynamicLinkerExporter классы, видимые для этого загрузчика классов, и объединить предоставленные ими привязки в DynamicLinker , который он создаёт. Это позволяет взаимодействовать между языками: если у вас есть две языковые среды выполнения A и B, развернутые в вашей JVM, и они экспортируют свои привязки через вышеупомянутый механизм, языковая среда выполнения A будет иметь экземпляр языкоспецифической привязки из B и наоборот внутри их DynamicLinker объектов. Это означает, что если объект из языковой среды выполнения B передаётся в код из языковой среды выполнения A, привязка из B получит возможность привязать узел вызова в A, когда встретит объект из B.- Граф модулей:
- С тех пор:
- 9
Пакеты
| Пакет | Описание |
|---|---|
| jdk.dynalink | Содержит интерфейсы и классы, используемые для связывания места вызова invokedynamic. |
| jdk.dynalink.beans | Содержит линкер для обычных объектов Java. |
| jdk.dynalink.linker | Содержит интерфейсы и классы, необходимые средам выполнения языков для реализации собственных языковых моделей объектов и преобразований типов. |
| jdk.dynalink.linker.support | Содержит классы, которые упрощают реализацию языковыми средами выполнения собственных языковых моделей объектов и преобразований типов, предоставляя базовые реализации некоторых классов, а также различные утилиты. |
| jdk.dynalink.support | Содержит классы, которые делают использование Dynalink более удобным, предоставляя базовые реализации некоторых классов, а также различные утилиты. |
Сервисы
| Тип | Описание |
|---|---|
| GuardingDynamicLinkerExporter | Класс, действующий как поставщик защитных динамических линков, которые могут быть автоматически загружены другими средами выполнения языков. |
© 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/jdk.dynalink/module-summary.html