Модуль jdk.dynalink

Определяет API для динамической привязки высокоуровневых операций к объектам.

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") выражает операцию получения свойства с именем "color".

Операции с несколькими пространствами имён

Некоторые языки могут не иметь отдельных пространств имён на объектах для свойств, элементов и методов, и конструкт исходного языка может обращаться к нескольким из них одновременно. Dynalink поддерживает указание нескольких объектов Namespace с NamespaceOperation.

Языковые привязки

Языки, которые определяют свою собственную модель объектов, отличную от модели объектов на основе классов JVM, и/или используют собственные преобразования типов, должны создавать собственные языковые привязки. См. пакет jdk.dynalink.linker и конкретно интерфейс GuardingDynamicLinker для начала.

Dynalink и объекты Java

Объекты DynamicLinker , созданные DynamicLinkerFactory по умолчанию, содержат внутренний экземпляр BeansLinker, который является языковой привязкой, реализующей обычную семантику Java для всех вышеперечисленных операций и может привязывать любые объекты Java, которые не удалось привязать никакой другой языковой привязке. Таким образом, все среды выполнения языков имеют встроенную совместимость с обычными объектами Java. См. BeansLinker для получения подробной информации о том, как он привязывает различные операции.

Взаимодействие между языками

A DynamicLinkerFactory может быть сконфигурирован с помощью загрузчика классов. Он будет пытаться создать все классы GuardingDynamicLinkerExporter, видимые для этого загрузчика, и объединить предоставляемые ими привязки в DynamicLinker , который он создаёт. Это позволяет взаимодействовать между языками: если у вас есть две среды выполнения языков A и B, развернутые в вашей JVM, и они экспортируют свои привязки с помощью вышеуказанного механизма, среда выполнения языка A будет иметь экземпляр языковой привязки из B и наоборот внутри своих объектов DynamicLinker . Это означает, что если объект из среды выполнения языка B передаётся коду из среды выполнения языка A, привязка из B получит возможность привязать узел вызова в A при столкновении с объектом из B.
Граф модулей:
Module graph for jdk.dynalinkModule graph for jdk.dynalink
С:
9

Пакеты

Пакет Описание
jdk.dynalink

Содержит интерфейсы и классы, которые используются для привязки invokedynamic узла вызова.

jdk.dynalink.beans

Содержит привязку для обычных объектов Java.

jdk.dynalink.linker

Содержит интерфейсы и классы, необходимые средам выполнения языков для реализации собственных языковых моделей объектов и преобразований типов.

jdk.dynalink.linker.support

Содержит классы, которые делают более удобной реализацию собственных языковых моделей объектов и преобразований типов для сред выполнения языков путём предоставления базовых реализаций некоторых классов, а также различных утилит.

jdk.dynalink.support

Содержит классы, которые делают использование Dynalink более удобным, предоставляя базовые реализации некоторых классов, а также различные утилиты.

Сервисы

Тип Описание
GuardingDynamicLinkerExporter

Класс, выступающий в роли поставщика защищенных динамических линков, которые могут быть автоматически загружены другими языковыми средами выполнения.

© 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/jdk.dynalink/module-summary.html

Spec-Zone .ru
спецификации, руководства, описания, API