Библиотека сайта rus-linux.net
Среда времени выполнения динамических языков и языки Iron
Глава 8 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: The Dynamic Language Runtime and the Iron Languages
Автор: Jeff Hardy
Перевод: Н.Ромоданов
8.8. Протокол метаобъектов
Кроме того, инфраструктура языков, которая является другой ключевой частью среды DLR, дает возможность языку (основному языку или хост-языку) осуществлять динамические вызовы объектов, определенных на другом языке (языке исходного кода объекта). Чтобы это было возможно, среда DLR должна понимать, какие операции являются допустимыми над на объектом, независимо от того, на каком языке он был написан. В языках Python и Ruby есть довольно похожие модели объектов, но язык JavaScript имеет радикально другую систему типов, базирующихся на прототипах (в отличие от системы, базирующих на классах). Вместо того, чтобы пытаться объединять различные системы типов, среда DLR обрабатывает их так, как если бы все они базировались на принципах передачи сообщений (message passing) в стиле языка Smalltalk.
В объектно-ориентированной системе, базирующейся на передаче сообщений, объекты посылают сообщения другим объектам (как правило, с параметрами), а затем объект может в качестве результата возвращать другой объект. Таким образом, хотя в каждом языке есть свое собственное понимание о том, что может представлять собой объект, они все почти всегда рассматриваться как эквивалентные за счет вызовов методов просмотра, которые передаются в виде сообщений между объектами. Конечно, даже статические объектно-ориентированные языки подходят к этой модели с некоторой натяжкой; отличие динамических языков состоит в том, что вызываемый метод не должны быть известен во время компиляции, или может даже вообще не существовать в объекте (например, метод method_missing
языка Ruby), а у целевого объекта обычно есть возможность, если это потребуется, перехватить сообщение и обработать его по-своему (например, метод __getattr__
языка Python).
В среде DLR определяется следующие сообщения:
{Get|Set|Delete}Member
: операции для манипулирования с членами объекта{Get|Set|Delete}Index
: операций для индексированных объектов (например, массивов или словарей)Invoke, InvokeMember
: вызов объекта или члена объектаCreateInstance
: создание экземпляра объектаConvert
: преобразование объекта из одного типа в другойUnaryOperation, BinaryOperation
: выполнение операций с использованием операторов, таких как получение обратного значения (!) или сложение (+)
Этих операций, если взять их вместе, должно быть достаточно для реализации почти любой объектной модели языка.
Поскольку среда CLR наследует статическую типизацию, объекты динамических языков все еще необходимо выражать с помощью статических классов. Обычно это происходит с помощью статического класса, например, PythonObject
, а реальные объекты языка Python являются экземплярами этого класса или его подклассов. Чтобы обеспечить возможность совместного использования и хорошую производительность, в среде DLR применяется гораздо более сложный механизм. Вместо того, чтобы иметь дело с объектами конкретных языков, в среде DLR используются мета-объекты (meta-objects), которые являются подклассами класса System.Dynamic.DynamicMetaObject
и имеют методы для обработки всех перечисленных выше видов сообщений. В каждом языке есть свой собственный подкласс DynamicMetaObject
, реализующий модель объектов языка, например, класс MetaPythonObject
в языке IronPython. В метаклассах также есть соответствующие конкретные классы, реализующие интерфейс System.Dynamic.IDynamicMetaObjectProtocol
, в котором определено, как в среде DLR идентифицируются динамические объекты.
Рис.8.3: Диаграмма класса IDMOP
Из класса, в котором реализован протокол IDynamicMetaObjectProtocol
, среда DLR с помощью вызова метода GetMetaObject()
может получить объект DynamicMetaObject
. Этот объект DynamicMetaObject
будет предоставлен языком и будет реализован с помощью функций привязок, которые требуются самому объекту. У каждого объекта DynamicMetaObject
также есть значение и тип, если таковые есть в объекте, лежащем в его основе. Наконец, в объекте DynamicMetaObjec
t есть дерево выражений, в котором все еще хранятся точки вызовов и указаны ограничения на каждое выражение, аналогичные механизмам привязок точек вызова.
Когда среда DLR компилирует обращение к методу класса, определяемого пользователем, то сначала создается точка вызова (т.е. экземпляр класса CallSite
). В точке вызова инициируется процесс привязки так, как он описан выше в разделе «Точки динамических вызовов», в результате чего он в конечном итоге происходит вызов метода GetMetaObject()
в экземпляре класса OldInstance
, который возвращает объект MetaOldInstance
. В языке Python есть классы как старого, так и нового вида, но в данном случае это неважно. Затем вызывается средство привязки (а именно PythonGetMemberBinder.Bind()
), вызывающее, в свою очередь, метод MetaOldInstance.BindGetMember()
; он возвращает новый объект DynamicMetaObject
, который представляет собой новый механизм поиска имени метода объекта. После этого вызывается другое средство привязки PythonInvokeBinder.Bind(
), в котором вызывается метод MetaOldInstance.BindInvoke()
, представляющий собой обвертку первого объекта DynamicMetaObject
с новым способом вызова искомого метода. Здесь присутствует исходный объект, дерево выражений, используемого для поиска имени метода, и объекты DynamicMetaObject
, представляющие собой аргументы, используемые в методе.
Как только в выражении будет построен объект DynamicMetaObject
, его дерево выражений и ограничения будут использованы для создания делегата, затем возвращаемого в точку вызова, которая инициировала процесс привязки. С этого момента код можно сохранять в кэшах точки вызова, что позволяет выполнять операции над объектами также же быстро, как выполняются другие динамические вызовы, и почти также быстро, как выполняются статические вызовы.
Хост-языки, в которых требуется выполнять динамические операции для динамических языков, должны создавать соответствующие привязки из метапривязки DynamicMetaObjectBinder
. DynamicMetaObjectBinder прежде, чем возвращаться к семантике привязок хост-языка, сначала запросит целевой объект, к которому привязывается операция (с помощью вызова метода GetMetaObject()
и выполнения процесса связывания, описанного выше). В результате, если объект языка IronRuby будет доступен из программы IronPython, то привязка сначала будет происходить в семантике языка Ruby (целевой язык); а если этого сделать не удасться, то привязка DynamicMetaObjectBinder
вернется к семантике языка Python (хост-язык). Если объект, для которого делается привязка, не динамический (то есть, в нем не реализован провайдер IDynamicMetaObjectProvider
), как, например, классы из базовой библиотеки платформы .NET, то для доступа к нему с семантикой хост-языка используется механизм .NET reflection.
В языкам предоставлена относительная свобода реализации этого механизма; реализация PythonInvokeBinder
в языке IronPython не выводится из InvokeBinder
, т.к. для объектов языка Python нужно выполнять некоторую специальную дополнительную обработку. Пока это касается только объектов языка Python, то никаких проблем не возникает; если встречается объект, в котором реализован провайдер IDynamicMetaObjectProvider
, и он не является объектом языка Python, то такой объект перенаправляется в класс CompatibilityInvokeBinder
, который наследуется от класса InvokeBinder
и который может правильно обрабатывать чужие объекты.
Если при возвращении к семантике хост-языка выполнить привязку операцию не удается, исключительное состояние не создается; вместо этого возвращается объект DynamicMetaObject
, представляющий собой ошибку. Затем средство привязки хост-языков обработает его так, как это делается в самом хост-языке; например, доступ к отсутствующему члену объекта языка IronPython из гипотетической реализации JavaScript может вернуть неопределенное значение undefined
, тогда как то же самое действие в объекте языка JavaScript из IronPython будет причиной возникновения ошибки AttributeError
.
Возможность языков работать с динамическими объектами практически бесполезна без возможности языков загружать и выполнять код, написанный на других языках, а для этого в среде DLR для других языков предоставляется единый механизм.
Продолжение статьи: Хост-среда