Delphi - как определяется номер строки исходника ?

Тема в разделе "WASM.RESEARCH", создана пользователем yunus, 18 дек 2004.

  1. yunus

    yunus New Member

    Публикаций:
    0
    Регистрация:
    18 дек 2004
    Сообщения:
    11
    Адрес:
    Russia
    Подскажите пожалуйста ответ.

    Задача такая - написать в Delphi (семерка) процедуру выводящую номер строки исходника с которой она была вызвана (ну, что-то подобное Assert). Единственное до чего удалось докопаться - Assert получает необходимую информацию при компиляции, но механизм под вопросом.

    Забавно - самой процедыры Assert в модуле System нет.
     
  2. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    yunus

    самой процедыры Assert в модуле System нет

    Ты невнимателен. Source\Rtl\Sys\System.pas строки с 11477 по 11515
    Код (Text):
    1. procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
    2. {$IFDEF PUREPASCAL}
    3. begin
    4.   if Assigned(AssertErrorProc) then
    5.     AssertErrorProc(Message, Filename, LineNumber, Pointer(-1))
    6.   else
    7.     Error(reAssertionFailed);  // loses return address
    8. end;
    9. {$ELSE}
    10. asm
    11.         PUSH    EBX
    12. {$IFDEF PIC}
    13.         PUSH    EAX
    14.         PUSH    ECX
    15.         CALL    GetGOT
    16.         MOV     EBX, EAX
    17.         MOV     EAX, [EBX].AssertErrorProc
    18.         CMP     [EAX], 0
    19.         POP     ECX
    20.         POP     EAX
    21. {$ELSE}
    22.         CMP     AssertErrorProc,0
    23. {$ENDIF}
    24.         JNZ     @@1
    25.         MOV     AL,reAssertionFailed
    26.         CALL    Error
    27.         JMP     @@exit
    28.  
    29. @@1:    PUSH    [ESP+4].Pointer
    30. {$IFDEF PIC}
    31.         MOV     EBX, [EBX].AssertErrorProc
    32.         CALL    [EBX]
    33. {$ELSE}
    34.         CALL    AssertErrorProc
    35. {$ENDIF}
    36. @@exit:
    37.         POP     EBX
    38. end;
    39. {$ENDIF}




    механизм под вопросом

    Поставь точку останова перед вызовом Assert и посмотри в окне cpu как он оформлен. Увидишь что-то вроде
    Код (Text):
    1. mov ecx,константа_с_номером_строки
    2. mov edx,адрес_строки_с_именем_модуля
    3. mov eax,адрес_строки_с_сообщением
    4. call @Assert
    до этих инструкция расположена проверка условия вызова. Т.е. номер строки фигурирует в программе как константа, определенная компилятором.



    Afaik в delphi нет макросов аналогичных Си’шным __FILE__ и __LINE__ и получить их красиво не получится, я не знаю и не красивого способа.
     
  3. yunus

    yunus New Member

    Публикаций:
    0
    Регистрация:
    18 дек 2004
    Сообщения:
    11
    Адрес:
    Russia
    Да, спасибо, до этого я докопался (при этом мне таки кажется assert и _assert две разные беды).

    По моему соображению компилятор встретив в теле проги обращение к Assert формирует следующий код - заполняет три регистра и вызывает _Assert из System. Переиначу вопрос таким образом - как этот компилятор узнает, что перед ним именно Assert (не по имени же !) и нельзя ли как-нибудь прикинуться этим Assert-ом и заставить Delphi сгенерить для моей процедурки именно такой код ?
     
  4. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    yunus

    мне таки кажется assert и _assert две разные

    Трассируй вызов call @Assert и поймешь, что @Assert == _Assert == Assert.



    как-нибудь прикинуться этим Assert-ом

    Можно в качестве AssertErrorProc задать адрес своей процедуры.



    заставить Delphi сгенерить для моей процедурки именно такой код

    Какой код?
     
  5. yunus

    yunus New Member

    Публикаций:
    0
    Регистрация:
    18 дек 2004
    Сообщения:
    11
    Адрес:
    Russia
    трасировал и получил вот что -


    Код (Text):
    1. DelphiUnit.PAS.232 Assert(not condition, message);
    2. test bl,bl
    3. jz TAssert.IsFalse + $36
    4. mov ecx, $000000e8
    5. mov edx, $01174db8
    6. call @Assert
    7. xor eax, eax
    8. pop edx




    - то есть Assert всетаки не просто вызов _Assert а что-то наподобии служебного слова. Встретив этот "Assert" в тексте программы компилятор творит совершенно нестандартный код вызова - при этом один из аргументов (condition) вообще не передается в процедуру - а проверяется до вызова (test bl, bl), зато передаются два скрытых параметра (номер строки и имя модуля) - известные к сожалению только компилятору.

    Когда я "мечтал" прикинуться Assert-ом, я имел ввиду - состряпать такую процедуру которая вызвала бы у компилятора такую же реакцию, и которой он бы сформировал именно такой код вызова.