Если вы поэкспериментируете с внутренностями Delphi, вы обнаружите что-то странное и явно недокументированное в записях TTypeInfo, сгенерированных компилятором. Если PTypeInfo указывает на запись TTypeInfo по адресу X, в X - 4
вы найдете следующие 4 байта, описывающие указатель на X. Например:
procedure test(info: PTypeInfo);
var
addr: cardinal;
ptr: PPointer;
begin
addr := cardinal(info);
writeln('addr: ', addr);
dec(addr, 4);
ptr := PPointer(addr);
addr := cardinal(ptr^);
writeln('addr: ', addr);
end;
Передайте любой допустимый PTypeInfo, сгенерированный компилятором, в эту подпрограмму, и он дважды выведет один и тот же адрес. Я немного покопался в TypInfo.pas, но не вижу ничего, что упоминало бы этот «указатель идентичности» или для чего он там. Кто-нибудь знает, почему это там? Похоже, что это верно для каждой версии Delphi от D3 до D2010.
Все очень просто: пакеты и динамическое связывание.
BPL - это DLL. DLL связываются через исправляемые таблицы, а не весь код в EXE или DLL связывается с исправляемой DLL (что нанесло бы большой вред совместному использованию памяти только для чтения несколькими процессами). Чтобы предотвратить необходимость ссылки на TypeInfo(SomeType)
где-то в коде, или typeinfo EXE или DLL, модифицируемых при линковке с BPL, вместо этого существует перенаправление через таблицу импорта.
Легко увидеть разницу между статическим линкованием и линкованием по BPL в этой программе:
{$apptype console}
uses TypInfo, SysUtils;
type
TFoo = class(TObject);
var
x: PPTypeInfo;
begin
x := GetTypeData(TypeInfo(TFoo))^.ParentInfo;
Writeln(x^^.Name);
Writeln(Format('x %p', [x]));
Writeln(Format('x^ %p', [x^]));
end.
На моей локальной машине при компиляции с помощью dcc32 test.pas
выводится:
TObject
x 00401B64
x^ 00401B68
Но при компиляции с помощью RTL-пакета с помощью dcc32 -LUrtl test.pas
выводится:
TObject
x 004051F0
x^ 40001DA4
Надеюсь, это прояснит ситуацию.
Не совсем понимаю, что происходит, но если вы посмотрите, например, на IsPublishedProp
в блоке TypInfo
, вы увидите видите, что он приводит ClassInfo экземпляра как указатель на структуру TypeInfo:
PTypeInfo(Instance.ClassInfo)
Когда вы смотрите на метод ClassInfo, он возвращает простой указатель, значение которого кажется связанным с таблицей vmt:
Result := PPointer(Integer(Self) + vmtTypeInfo)^;
vmtTypeInfo
имеет значение -72. За четыре байта до -76 будет vmtInitTable
. За vmtTypeInfo идут FieldTable, MethodTable, DynamicTable и т. д.
значение vmtInitTable используется, например, в TObject.CleanupInstance
и передается в _FinalizeRecord
как указатель на структуру TypeInfo.
Таким образом, четыре байта перед структурой TypeInfo, указывающей на структуру TypeInfo, кажутся там по замыслу и являются частью структуры vmt.
Править
Как правильно заметил Мейсон, вышеизложенное - это полный отвлекающий маневр (см. Комментарии).Я оставляю ответ, чтобы другим не пришлось его искать.
Обновление Чтобы избежать путаницы с переменными и их адресами, я переписал процедуру тестирования Мейсона следующим образом:
procedure test(info: PTypeInfo);
begin
writeln('value of info : ', cardinal(info));
writeln('info - 4 : ', cardinal(info) - 4);
writeln('value 4 bytes before: ', cardinal(PPointer(cardinal(info)-4)^));
end;
и вызвал ее со следующей информацией:
procedure TryRTTIStuff;
begin
writeln('TPersistent');
test(TypeInfo(TPersistent));
writeln('TTypeKind enumeration');
test(TypeInfo(TTypeKind));
writeln('Integer');
test(TypeInfo(Integer));
writeln('Nonsense');
test(PTypeInfo($420000));
end;
Первые три дают результаты, которые описывает Мейсон. Я только добавил дополнительную запись, чтобы показать значение указателя для последней записи. Последний вызов в TryRTTIStuff - показать, что, когда вы не передаете указатель на действительную структуру TypeInfo, вы не получаете одинаковое значение в первой и третьей записи для вызова.
Пока нет сведений о том, что происходит с TypeInfo. Может быть, нам стоит спросить Барри Келли, поскольку он является автором нового D2010 RTTI, поэтому нам также следует много знать о старом ...
возможно, это связный список, который находится в непрерывной памяти:)