New Page 2
TObject (ClassNameIs,
паскаль, ассемблер)
На прошлом уроке мы изучили метод
ClassName класс TObject.
Сегодня продолжим изучать класс TObject. Давайте
посмотрим, как реализован метод ClassNameIs:
class function TObject.ClassNameIs(const Name: string): Boolean;
{$IFDEF PUREPASCAL}
var
Temp: ShortString;
I: Byte;
begin
Result := False;
Temp := ClassName;
for I := 0 to Byte(Temp[0]) do
if Temp[I] <> Name[I] then Exit;
Result := True;
end;
{$ELSE}
asm
PUSH EBX
XOR EBX,EBX
OR EDX,EDX
JE @@exit
MOV EAX,[EAX].vmtClassName
XOR ECX,ECX
MOV CL,[EAX]
CMP ECX,[EDX-4]
JNE @@exit
DEC EDX
@@loop:
MOV BH,[EAX+ECX]
XOR BH,[EDX+ECX]
AND BH,0DFH
JNE @@exit
DEC ECX
JNE @@loop
INC EBX
@@exit:
MOV AL,BL
POP EBX
end;
{$ENDIF}
|
Как видим, данная функция просто напросто берет имя класса при помощи уже
знакомой нам функции ClassName и побайтно сравниваем его с аргументом.
Спрашивается, а почему бы просто не сравнить строки? Зачем нужен этот цикл:
for I := 0 to Byte(Temp[0])
do
if Temp[I] <> Name[I] then Exit; |
Этот вопрос задал себе и я. Сперва подумал, что дело в том,
что при простом сравнении сравнивается каждый байт строки, а здесь только до
момента первого несовпадения. Теоретически это должно работать быстрее. Но сразу
возникает вопрос: а что если параметр Name окажется
длиннее, но его начало полностью совпадет с именем класса? Иными словами, пусть
у нас класс назван "TMyClass", а мы производим
сравнение с "TMyClass1" и у нас будет результат
true, что явно ошибочно.
Для того, что бы разобраться, я поискал информацию в
Интернете, позадавал вопросы на форумах и выяснил, что код на паскале ошибочный,
видимо, из за того, что перекомпилировать исходные модули, нужно хорошо
извратиться, а по умолчанию модули скомпилированы именно с отключенной
директивой
PUREPASCAL, вот и не тестировали разработчики
Delphi достаточно хорошо эту часть кода.
Теперь попробуем расшифровать ассемблерный код что бы
убедиться, что он действительно работает правильно:
PUSH EBX
XOR EBX,EBX // Result := False
OR EDX,EDX // if Self==nil
JE @@exit // then Exit // этого на
паскале нет
MOV EAX,[EAX].vmtClassName // tmp :=
Self.ClassName
XOR ECX,ECX // L :=0
MOV CL,[EAX] // L := tmp[0]
CMP ECX,[EDX-4] // if L <> Length(Name)
JNE @@exit // then Exit // этого
тоже нет
DEC EDX
@@loop: // for i:= L downto 1 do
MOV BH,[EAX+ECX] // if ((Temp[I]
XOR BH,[EDX+ECX] // xor Name[I])
AND BH,0DFH // and $DF)
// этого тоже нет
JNE @@exit // <>0
then Exit;
DEC ECX
JNE @@loop
INC EBX // Result := true
@@exit:
MOV AL,BL
POP EBX
|
|