1. Pascal / Говнокод #11395

    +99

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    // Получение имени выполняемого метода, вызывать можно только из Published-методов класса.
    // Для обычных методов: FindClassMethodNames(ClassType()), для статических методов FindClassMethodNames(self).
    {$optimization OFF}
    function FindClassMethodNames(obj: TClass): string;
    var _AdrPtr: Pointer;
    begin
      asm
        mov eax, obj
        mov edx, dword ptr [esp + 272]
        push ebx
        push esi
        push edi
        push $7FFFFFFF
        xor edi, edi
        jmp @@haveVMT
       @@outer:
        mov eax, dword ptr [eax]
       @@haveVMT:
        mov esi, dword ptr [eax].vmtMethodTable
        test esi, esi
        je @@parent
        movzx ecx, word ptr [esi]
        add esi, 2
       @@inner:
        pop ebx
        push edx
        sub edx, dword ptr [esi + 2]
        jl @@no1
        cmp edx, ebx
        jg @@no1
        mov ebx, edx
        mov edx, dword ptr [esi + 2]
        mov edi, edx
       @@no1:
        pop edx
        push ebx
        movzx ebx, word ptr [esi]
        add esi, ebx
        dec ecx
        jnz @@inner
       @@parent:
        mov eax, dword ptr [eax].vmtParent
        test eax, eax
        jne @@outer
        mov _AdrPtr, edi
        pop edi
        pop esi
        pop ebx
      end;
      Result := obj.MethodName(_AdrPtr);
    end;
    {$optimization ON}
    
    // пример использования
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowMessage(FindClassMethodNames(ClassType()));
    end;

    А есть нормальный способ получить имя выполняемого метода, и строку кода заодно?

    Запостил: ctm, 11 Июля 2012

    Комментарии (53) RSS

    • Шаблоны в паскале мы уже видели... теперь reflection...
      Ответить
    • > А есть нормальный способ получить имя выполняемого метода, и строку кода заодно?

      Думаю, если переписать это с кулхацкерского языка на нормальный, то будет проще.
      Ответить
      • толку-то? все-равно принцип - получение из стека адреса возврата и поиск ближайшей по адресу функции в VMT. К тому же, номер строки и имя модуля так не получить.
        Ответить
    • >{$optimization OFF}
      Выключили и без того слабую оптимизацию?
      А на ассемблере небось писали для оптимизации?
      Эпично.
      Ответить
      • Кстати, а делфи разве оптимизирует ассемблерные фрагменты?
        Ответить
        • А что-то их оптимизирует?
          Ответить
          • Насчет именно оптимизации - очень маловероятно. Но gcc, к примеру, умеет в некоторых случаях подбирать регистры, которые не будут мешать ему в оптимизации окружающего ассмблерную вставку кода (код умножения на 5 процитирован из мана, т.к. если честно, не люблю GNU ассемблер, и не до конца в нем разбираюсь):
            asm ("leal (%1,%1,4), %0"
                : "=r" (five_times_x)
                : "r" (x) 
            );

            Здесь gcc подберет удобные для окружающего кода регистры, и подставит их имена вместо %0 и %1. В какой-то степени это можно назвать оптимизацией...
            Ответить
            • Это, кажется, не GNU ни разу.
              Т.е., оно, конечно, сейчас называется GAS в некоторых мануалах. Но исторически это кое-что другое.
              Ответить
              • Ну синтаксис команд, если не изменяет память, взят из ассемблера AT&T, но все остальные фичи вроде описания входных\выходных аргументов и запорченных регистров, если не туплю, именно GNU.
                Ответить
            • Да, жаль, что я нихрена не понимаю в гну асме...
              Ответить
              • Ну там ничего особо сложного, просто очень накуренный AT&T'шный синтаксис.

                Вкратце:
                mov eax, ebx => movl %ebx, %eax
                mov eax, 1 => movl $1, %eax
                mov al, 5 => movb $5, %al
                mov eax, [ebx+100500] => movl 100500(%ebx), %eax
                mov eax, [ebx*4+edx+5] => movl 5(%edx, %ebx, 4), %eax
                Ответить
                • Да вроде бы да, но глаз в этих процентиках тонет и ускользает суть повествования, ну и ещё постоянно мысленно менять аргументы местами. Почему-то обычный асм воспринимается намного легче.
                  Ответить
                  • Не обычный, а Интел-синтаксис.
                    Ответить
                  • Да я разве спорю... Интеловский синтаксис намного удобнее.
                    Ответить
                    • А в ассемблере fasm немного изменённый, более ортогональный.

                      mov eax, myVar <- загружаем адрес myVar

                      mov eax, [myVar] <- загружаем значение по адресу myVar

                      mov eax, [myVar+1] <- КО: загружаем значение по адресу myVar + 1

                      Никаких MASM'овских OFFSET не нужно.
                      Ответить
        • Нет, и не должна. Но функция объявлена без директивы assembler, и оптимизатор волен что-нибудь соптимизировать.
          Ответить
      • > Выключили и без того слабую оптимизацию?

        Нормальная там оптимизация, если знать, что оптимизируется, а что - нет. Запарили этим стереотипом.

        >{$optimization OFF}

        Я так понимаю, это для того, чтобы гарантировать наличиа адреса возврата и указателя на строку на том месте, на котором их ждут - чтобы оптимизатор их не выкинул. Правда я не понимаю, как локалное отключиние оптимизации поможет компилятору догадаться, какие именно строки не надо выкидывать.
        Ответить
        • Скорее всего это связано с выделением стека под локальную переменную и Result.
          Ответить
      • на асме, т.к. нужно получить адрес из стека, откуда был произведен вызов этой функции.
        да и в TObject все реализовано подобным способом.
        оптимизация отключена, т.к. иначе оно что-то со стеком делает
        Ответить
    • > для статических методов FindClassMethodNames(self)
      Штоу?
      Ответить
    • > Получение имени выполняемого метода
      Кстати, а чего-то типа сишных __FILE__ __LINE__ и __FUNCTION__ в делфи нет?
      Ответить
      • Нашел - {$PROCNAME}. Но, в связи с отсутствием КРЕСТОБЛЯДСКОГО макропроцессора, удобного ее использования не предвидится..
        Ответить
        • если в делфи нет какой-то функциональности, значит она никому на самом деле и не нужна
          так что не надо тут клеветы
          Ответить
      • Так assert там и так эту информацию выдает, а где кроме assert эти макросы нужны?
        Ответить
        • а что, кроме того, что упасть по assert, у программы в делфи больше нет вариантов?
          просто вывести в лог и продолжить работу, например?
          Ответить
          • Поймать EAssertionFailed, вывести сообщение в лог и продолжить работу, например.
            Ответить
            • > А есть нормальный способ получить имя выполняемого метода, и строку кода заодно?

              я правильно понял, что только путем выкидывания и отлова на месте EAssertionFailed?
              Ответить
              • А есть нормальное применение имени выполняемого кода и строке заодно, кроме как в ассертах? Для ассерта достаточно кинуть assert(condition, 'Something wrong'). Ловить его на месте тоже не надо, можно ловить там же где все исключения.

                А для других целей - нету нормального метода, можно с ассемблером извращаться, можно строку EAssertionFailed парсить. И то и то - на любителя. В С вот нет нормального метода получить текст текущей процедуры.
                Ответить
                • если приложение не спрограммировано мышкой, а не имеет даже консоли, и тем более если оно работает на адской железяке, к которой доступ только на посмотреть логи в реальном времени, то видеть трейс, состоящий из "я исполняюся тута, у меня всё хорошо с такими параметрами" - обычное дело при отладке
                  компилятору виднее всех в каком файле, в какой строчке и в какой функции он сейчас транслирует код, поэтому замена __FILE__, __LINE__, __FUNCTION__ и других базовых фич на "давайте выкинем исключение, всё равно это ведь только для фатальных ситуаций надо", только лишь потому что компилятор этого не умеет - не только для фатальных

                  и да, что такое текст текущей процедуры? это же не интерпретатор
                  Ответить
                  • Новая идея для отладчика: режим, при котором все входы в процедуры и присваивания выводятся в лог-файл.
                    Только для функций, помеченных словом logged. Как правило надо только несколько функций проверить, то есть проще написать, какие надо логгировать, чем какие не надо.
                    В отличие от классического отладчика, не надо будет думать "ой бля что только что в этой переменной было, как назад отмотать". Одни преимущества.
                    Ответить
                    • > Только для функций, помеченных словом logged.

                      Так?
                      http://ideone.com/OzEWX
                      Ответить
                      • А это работает для любого количества аргументов и для любой структуры функции?
                        Ответить
                        • По идее да. В args попадают все позиционные параметры, в kwargs все именованные. Надо бы еще допилить обработку исключений, чтобы при их возникновении выводилось что-то в духе "{0} raised {1}".
                          Ответить
                        • Вот как-то так:
                          http://ideone.com/LkNKA
                          Ответить
                          • Пофиксил баг, из-за которого бектрейс выводился не полностью.
                            http://ideone.com/tq9bJ
                            Ответить
                            • Хорошо, а как вывести результаты всех операций, происходящих в функции?

                              И *- это типа раскрывател скобок такой, чтобы f(*(1,2)) перевести в f(1,2)?
                              Ответить
                              • > И *- это типа раскрывател скобок такой
                                Да
                                args = (1, 2)
                                kwargs = {'x': 5, 'y': 6}
                                f(*args, **kwargs) # == f(1, 2, x=5, y=6)
                                Это просто мега-полезная фича
                                Ответить
                              • > Хорошо, а как вывести результаты всех операций, происходящих в функции?
                                А вот это, увы, никак.
                                Ответить
                    • Здорово напоминает декоратор logged в питоне. Нестандарт вроде, но его кто только не велосипедил...
                      Интересно, я думаю в Си тоже можно что-то типа такого реализовать.

                      P.S.: Чорд, вот что значит полениться перейти по ссылке.
                      Ответить
                      • На макросах что ли? Да ну, жесть будет. Лучше на уровне среды и компилятора. Кстати, реализовать должно быть на порядок проще, чем настоящий пошаговый отладчик.
                        Ответить
                        • На макросах уже есть вменяемая реализация Фрэда Фиша (dbug):
                          http://sourceforge.net/projects/dbug/
                          Ответить

    Добавить комментарий