LINUX.ORG.RU

Написание плагина для Clang

 ,


1

5

Решил потыкать Clang API. Допустим, я хочу пробежаться по AST, найти некоторые места, которые удовлетворяют определённым критериям и модифицировать их.

С первыми двумя пунктами проблем особых нет - я создал плагин для Clang, который создаёт ASTConsumer, переопределил метод HandleToplevelDecl, который по условию запускает RecursiveASTVisitor на некоторых элементах. Наконец, я нахожу места, которые хотел бы модифицировать. А вот с модификацией есть проблемы.

Допустим, я знаю, что интересующие меня ноды являются дочерними элементами CompoundStmt. Я могу переопределить TraverseCompoundStmt, вручную бежать по его детям и вызывать TraverseStmt для них. При этом visitXXX устанавливает флаг, который я потом проверяю. При выполнении условия я могу присвоить *it (где it - итератор из диапазона от stmt->child_begin() до stmt->child_end()) новый узел. В принципе это работает. Например, я могу создать пустой CompoundStmt и заменить узел на него. После этого я вижу с помощью objdump (а также по дампу AST), что соответствующие инструкции исчезают. Но что если я хочу более сложных изменений?

Например, я хочу создать метку (LabelStmt). И тут я натыкаюсь на проблему, что LabelStmt хочет LabelDecl, который в свою очередь хочет IdentifierInfo. Каким образом создать новый идентификатор и получить его IdentifierInfo? Если указать вместо нормального IdentifierInfo nullptr, то clang предсказуемо падает. Каких-либо нормальных конструкторов или статических методов для создания IdentifierInfo в самом IdentifierInfo я не заметил. Значит IdentifierInfo должен создавать кто-то другой. Но кто? Как вообще генерировать новые идентификаторы?

★★★★★

Последнее исправление: KivApple (всего исправлений: 1)

К сожалению, AST не считается изменяемым пользователем/плагином.

Самый простой способ — сделать текстовое переписывание исходника (т.к. каждый узел AST показывает, за какой кусок текста программы он отвечает) и скомпилировать новый исходник.

vzzo ★★★
()
Ответ на: комментарий от vzzo

1) Я таки разобрался как сделать идентификатор. Можно вызвать Context->Idents.get(«имя»). Он создаёт идентификатор, если не может найти. Не знаю насколько корректно, но во всяком случае clang не падает, если подсунуть такой IdentifierInfo.

2) Да, похоже на то, что оно не очень рассчитано на изменение (вывод после чтения кучи веб-страниц). Чисто технически это возможно (хотя, скажем, для создания новых классов там надо делать какую-то жесть), но API может измениться в любой момент и придётся опять разбираться без вменяемой документации. Или всё не так страшно, проблема лишь в необходимости разбираться без документации?

А есть варианты кроме изменения исходника? Просто выглядит это как-то не очень рациональным, что потом он будет парсится абсолютно с нуля (а парсинг C++ это сложно, я это вижу по компиляции плагина для Clang - компиляция очень долгая, а автокомплит IDE глючит из-за огромных инклюдов). Опять же непонятно, что будет с инклюдами (а я хочу обработать всё, включая то, что заинклюдилось). Можно ли получить какое-то промежуточное представление, которое проще парсить, чем целый исходник, но которое потом может сожрать CLang?

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Или всё не так страшно, проблема лишь в необходимости разбираться без документации?

Проблема в основном в том, что некоторые изменения просто невозможно сделать из-за того, что их нужно делать одновременно в двух местах, и второе место начинает падать с ассертом после изменения первого. Так как AST у clang скорее не синтаксическое, а семантическое, то сюрпризов таких может быть много (вроде того, что разыменование указателя — это один тип узла, а вызов перегруженного унарного оператора* — это совершенно другой тип узла). А потом приходят шаблоны и начинается полнейший беспредел :)

Способ адекватно менять какое-либо внутреннее представление я не нашёл :( Но был какой-то проект, если я не ошибаюсь, занимавшийся автораспараллеливанием, у которого вполне получалось генерить новые узлы в AST, там была даже вспомогательная библиотека. К сожалению, сходу сейчас не нашёл.

vzzo ★★★
()
Ответ на: комментарий от vzzo

Теоретически можно сдампить AST в некий бинарный формат (clang так умеет), вручную его редактировать (разумеется, никакие assert'ы мешать уже не будут), а потом изменённый файл скормить clang. Вопрос в том, что этот формат может оказаться гораздо более нестабильным, чем Clang API.

Насчёт автораспараллеливания - я вот на что наткнулся: http://llvm.org/devmtg/2013-04/krzikalla-slides.pdf. Это оно? И тоже не могу найти исходники их StmtEditor.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от vzzo

Кстати, раз уж ты, как я понимаю, имел дело с Clang на таком уровне, ответь на вопрос. Говорят, Clang C API урезанный по сравнению с Clang C++ API. Насколько он урезанный? Чего там нет? Именно не в плане удобства использования, а в плане того, что там нет чего-то очень важного.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

А есть варианты кроме изменения исходника?

Можно сгенерировать новый исходник со своими «добавками», так например moc-ng работает

а парсинг C++ это сложно, я это вижу по компиляции плагина для Clang

там шаблонов много, если код более простой, то он парсится быстрее

annulen ★★★★★
()
Ответ на: комментарий от KivApple

сдампить AST в некий бинарный формат (clang так умеет), вручную его редактировать (разумеется, никакие assert'ы мешать уже не будут), а потом изменённый файл скормить clang

Проблема ассертов всё равно глобально не уйдёт, так как можно сгенерировать дерево, не отвечающее каким-нибудь недокументированным внутренним инваривантам.

vzzo ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.