From Novosibirsk, Russia? Our tiny company is looking for current or future rock-star developers.

February 07, 2008

Как работает статический анализ DTL: дизайн движка анализа

(Предполагается, что вы прочти предыдущий пост про алгоритмы.)

В основе всего лежит движок анализа, умеющий вычислять цели (goal'ы). Список целей есть в предыдущем посте — это, например, тип данной ноды, тип данной локальной пемеренной или список вызвавших данный метод.

Анализ начинается с корневой цели, которая передается методу AnalysisEngine.evaluate. При вычислении цели у нее вызывается Goal.evaluate, которому передается объект ContinuationRequestor.

Goal.evaluate (естественно, Goal — это интерфейс, далее я о таком упоминать не буду) может либо вычислить цель (сохранив результаты в ней же) и спокойно завершиться, либо создать объект, реализующий Continuation, и передать его в ContinuationRequestor.subgoal(Continuation).

Continuation, будучи создан Goal'ом, имеет два метода: provideSubgoals и done. Метод provideSubgoal возвращает (вернее, передает переданному ему requestor'у, но это не важно) набор Goal'ов, которые он хочет вычислить. Когда все те Goal'ы вычислены, у предоставившего их Continuation'а вызывается Continuation.done(ContinuationRequestor). Предполагается, что Continuation сохранил goal'ы внутри себя и Continuation.done() может получить из них результаты.

Continuation.done(ContinuationRequestor), аналогично Goal.evaluate(ContinuationRequestor), может либо вычислить результаты goal'а и спокойно завершиться, либо создать новый Continuation.

В результате мы имеем архитектуру, в которой цели могут запускать рекурсивное вычисление подцелей. При этом этап вычислений естественно запоминается в виде объектов (если запороть дизайн в этом месте, goal'ы превращаются в машины состояний с трудно отслеживаемым временем жизни полей), и не теряется информация о типе подцелей (что отражается на реализации Continuation.done).

В идеале AnalysisEngine проходится по goal'ам и их continuation'ам в ширину (т.е. сначала проходит по всем имеющимся goal'ам и continuation'ам, вызывая у них evaluate или done; затем проходит по созданным на предыдущем проходе continuation'ам, и т.д.) Однако в нашей реализации производился обход в глубину из-за некоторых утилитарных причин.

No comments: