Разбор кода C # (в виде строки) и вставка дополнительных методов - PullRequest
11 голосов
/ 15 февраля 2011

У меня есть приложение на C #, над которым я работаю, который загружает его код удаленно, а затем запускает его (ради аргумента вы можете считать приложение безопасным).

Код - C #, но он отправляется в виде XML-документа, анализируется как строка, а затем компилируется и выполняется.

Теперь то, что я хотел бы сделать - и у меня немного больше трудностей, чем я ожидал - - иметь возможность проанализировать весь документ, а перед компиляцией вставлять дополнительные команды после каждого выполнения строки.

Например, рассмотрим код:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCode
{
    static class MyProg
    {
        static void Run()
        {
            int i = 0;
            i++;

            Log(i);
        }
    }
}

Что бы я хотел, после разбора это что-то вроде:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCode
{
    static class MyProg
    {
        static void Run()
        {
            int i = 0;
            MyAdditionalMethod();
            i++;
            MyAdditionalMethod();

            Log(i);
            MyAdditionalMethod();
        }
    }
}

Имейте в виду очевидные подводные камни - я не могу просто иметь их после каждой точки с запятой, потому что это не будет работать в методе получения / установки, т. Е .:

Преобразование:

public string MyString { get; set; }

Кому:

public string MyString { get; MyAdditionalMethod(); set; MyAdditionalMethod(); }

потерпит неудачу. Как и объявления уровня класса, использование операторов и т. Д. Кроме того, есть ряд случаев, когда я мог бы также добавить MyAdditionalMethod () после фигурных скобок - как в делегатах, сразу после операторов if или объявлений методов и т. Д.

Итак, то, что я искал в CodeDOM, и похоже, что это может быть решением, но сложно понять, с чего начать. В противном случае я пытаюсь разобрать все это и создать дерево, через которое я могу разобрать - хотя это немного сложно, учитывая количество случаев, которые мне нужно рассмотреть.

Кто-нибудь знает какие-либо другие решения, которые существуют?

Ответы [ 5 ]

7 голосов
/ 27 февраля 2011

Есть несколько C # парсеров, которые я бы рекомендовал использовать что-то из Mono или SharpDevelop, так как они должны быть современными.Я попробовал использовать NRefactory от SharpDevelop, если вы скачаете исходный код для SharpDevelop, есть демоверсия и некоторые UnitTests, которые хорошо знакомят с его использованием.

Вам нужно улучшить посетителя, чтобы справиться с большим количеством дел, но это хорошее начало.

В качестве альтернативы вы можете скомпилировать оригинал и выполнить некоторые манипуляции с IL, используя Cecil или попробовать некоторую библиотеку AOP, например PostSharp .Наконец, вы можете взглянуть на .NET Profiling API .

3 голосов
/ 26 февраля 2011

Вы можете использовать систему преобразования программ от источника к источнику. Такой инструмент анализирует код, строит и AST, позволяет применять преобразования, а затем восстанавливает текст из AST. Что делает систему «источник-источник» приятной, так это то, что вы можете писать преобразования с точки зрения синтаксиса исходного языка, а не фрактальной детализации AST, что делает их намного проще для написания и понимания позже.

То, что вы хотите сделать, было бы смоделировано довольно простым программным преобразованием используя наш DMS Software Reingineering Toolkit :

rule insert_post_statement_call(s: stmt): stmt -> stmt =
   " \s " -> " { \s ; MyAdditionalMethod();   }";

Это правило не является "текстовой" заменой; скорее он анализируется синтаксическим анализатором, который обрабатывает целевой код, и, таким образом, фактически представляет два AST, слева и справа (разделенные синтаксисом «->». Кавычки не являются строковыми кавычками; они это кавычки вокруг синтаксиса целевого языка, чтобы отличить его от синтаксиса самого языка правил. Внутри кавычек находится текст целевого языка (например, C #) с экранированными символами типа \ s, которые представляют целые языковые элементы (в данном случае, stmt в соответствии с грамматикой целевого языка (например, C #). В левой части написано «соответствует любому утверждению s», поскольку s определено как «stmt» в грамматике. В правой части написано «замените выражение на блок, содержащий исходный оператор \ s и новый код, который вы хотите вставить ". Все это делается с точки зрения синтаксических деревьев с использованием грамматики в качестве руководства; он не может применить преобразование к чему-либо, что не является оператором. [ Причина, по которой оператор переписывается как блок, заключается в том, что в этом случае правая сторона равна val. ID, где утверждения действительны, иди проверьте свою грамматику.]

На практике вам нужно будет написать правила для обработки других особых случаев, но в основном это написание большего количества правил. Вам также необходимо упаковать анализатор / transformer / prettyprinter в пакет, который требует некоторого процедурного клея. Это по-прежнему гораздо проще, чем пытаться писать код, чтобы надежно взбираться по дереву вверх, вниз по узлам, а затем разбивать эти узлы, чтобы получить то, что вы хотите. Лучше, когда ваша грамматика (неизменно) должна быть скорректирована, правила переписывания переопределяются в соответствии с пересмотренной грамматикой и продолжают работать; Какое бы процедурное лазание по деревьям вы ни делали, оно почти наверняка сломается.

По мере того, как вы пишете все больше и больше преобразований, эта возможность становится все более и более ценной. И когда вы успешно справляетесь с небольшим количеством преобразований, добавление большего количества становится быстро привлекательным.

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

1 голос
/ 26 февраля 2011

Для парсинга вы можете использовать CSharpCodeProvider Class Parse ().

1 голос
/ 26 февраля 2011

После того, как вы проанализируете текст, на этой странице будет много полезной информации о динамической компиляции и выполнении кода: http://www.west -wind.com / Presentations / dynamiccode / dynamiccode.htm

1 голос
/ 15 февраля 2011

Вам нужно использовать деревья выражений. Немного полезной информации из MSDN для начала:

...