Страницы

Поиск по вопросам

воскресенье, 22 декабря 2019 г.

Как добавить инструкцию перед каждой строкой кода в методе, не меняя код?

#c_sharp


Хочу чтобы такой код

[Dotter]
static void Main(string[] args)
{            
    int y = 3,  x= 2;         
    for (int i = 0; i < 10; i++)
    {            
        y += x;
    }            
    Console.WriteLine(y);            
    Console.ReadKey();
}


Работал, как такой

static void Main(string[] args)
{
    Console.WriteLine(".");
    int y = 3, x = 2;
    Console.WriteLine(".");
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(".");
        y += x;
    }
    Console.WriteLine(".");
    Console.WriteLine(y);
    Console.WriteLine(".");
    Console.ReadKey();
    Console.WriteLine(".");
}


Реально это сделать и как?
    


Ответы

Ответ 1



Не меняя кода метода, т. е. в рантайме этого сделать нельзя. Ну по крайней мере, если не писать полноценный декомпилятор. Всё из-за того, что компилятор довольно много всего додумывает сам и даже если суметь каким-то образом выявить (не знаю, насколько это возможно) границы операторов (statement), чтобы перед каждым из них что-то вставить, то на простейшем foreach уже будет несоответствие - вместо foreach (var x in a) Console.WriteLine(x); ты увидишь вот такое: IEnumerator enumerator = a.GetEnumerator(); try { while (enumerator.MoveNext()) { int x = enumerator.Current; Console.WriteLine(x); } } finally { if (enumerator != null) { enumerator.Dispose(); } }

Ответ 2



Легкого варианта нету. Полноценный трейс делается через дебаггер, на дебаггер есть ограничение - сам себя делать дебаг - нельзя. Как написать трейсер на с++ c# и debug режим так же там найдете ссылки на с# примеры. Лучший вариант - пропустить код через текстовую "мясорубку" - добавить нужную строку в нужных местах кода, и пересобрать утилитой msbuild. Директива #line поможет сохранить нумерацию строк. Рефлексия, ручной мини-трейсер. Приведу пример мини-трейсера, который кое-как трассирует данный код. В mi - передать результат ф-ции где-то так typeof(My).GetMethod("Main") Main сделать public, или познакомится с рефлексией ближе. void Invoker(MethodInfo mi) {// TODO: Добавить проброс аргументов MethodBody b = mi.GetMethodBody(); MethodBase mb = null; byte[] il = b.GetILAsByteArray(); object[] stack = new object[b.MaxStackSize]; object[] locals = new object[b.LocalVariables.Count]; int sp = 0; // Указатель стека int pc = 0; // Указатель кода int i = 0, tok = 0; while (pc < il.Length){ // Добавить Console.Write("."); например switch (il[pc++]) // пли { // TODO: добавить обработку опкодов case 0: continue; case 6: stack[sp++] = locals[0]; continue; case 7: stack[sp++] = locals[1]; continue; case 8: stack[sp++] = locals[2]; continue; case 0x0A: locals[0] = stack[--sp]; continue; //stloc.0 case 0x0B: locals[1] = stack[--sp]; continue;//stloc.1 case 0x0C: locals[2] = stack[--sp]; continue;//stloc.2 case 0x16: stack[sp++] = 0; continue; // ldc.i4.0 case 0x17: stack[sp++] = 1; continue; // ldc.i4.1 case 0x18: stack[sp++] = 2; continue; // ldc.i4.2 case 0x19: stack[sp++] = 3; continue; // ldc.i4.3 case 0x1F: stack[sp++] = (int)il[pc++]; continue; // ldc.i4.n case 0x2B: pc += il[pc]+(il[pc] >= 0x80 ? -255 : 1); continue; case 0x2D: sp--; if ((stack[sp] is int) && ((int)stack[sp] > 0)) pc += il[pc++] + (il[pc] >= 0x80 ? -255 : 1); else pc++; continue; // case 0x2A: return; // ret case 0x32: sp -= 2; if ((int)stack[sp] < (int)stack[sp + 1]) pc += il[pc] + (il[pc]>=0x80 ? -255 : 1); else pc++; continue; // btl.s case 0x58: stack[sp - 2] = (int)stack[sp - 2] + (int)stack[sp - 1]; sp--; continue; // add грубо case 0x28: { // static call tok = BitConverter.ToInt32(il, pc); pc += 4; mb = mi.Module.ResolveMethod(tok); i = mb.GetParameters().Length; object[] pars = new object[i]; while (i > 0) pars[--i] = stack[--sp]; stack[sp++] = mb.Invoke(null, pars); if (mb.GetType().GetProperty("ReturnType").GetValue(mb,null) == typeof(void)) sp--; } continue; case 0x8C: pc += 4; continue; // box грубо case 0xFE: pc++; if (il[pc - 1] == 4) { stack[sp - 2] = ((int)stack[sp - 2] > (int)stack[sp - 1]) ? 1 : 0; sp--; };//clt грубо не проверял continue; } } } Для трассировки любого кода прийдется 1)дописать (реализовать) кучу инструкций. 2)познакомится с всеми видами токенов. 3)реализовать инициализацию локальных переменных 4)реализовать обработку исключений. Возможно на гите похожего монстра к-то выложил - не могу сказать, или что-то похожее можно найти в открытом доступе. Список инструкций есть тут http://en.wikipedia.org/wiki/List_of_CIL_instructions И долгими (зимними) вечерами можно наслаждаться отладкой этой ф-ции наблюдая как алгоритм трассирует код процедуры main.

Комментариев нет:

Отправить комментарий