Для чего нужен «указатель идентичности» перед TTypeInfo? - PullRequest
8 голосов
/ 09 августа 2010

Если вы покопаетесь во внутренностях 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.

Ответы [ 3 ]

11 голосов
/ 10 августа 2010

Все очень просто: пакеты и динамическое связывание.

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

Надеюсь, это все прояснит.

1 голос
/ 09 августа 2010

Не совсем понимаю, что происходит, но когда вы посмотрите, например, 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.

Edit

Как Мейсон справедливо указал вышеэто полная красная сельдь (см. комментарии).Я оставляю ответ, чтобы другим не пришлось его искать.

Обновление Чтобы избежать путаницы с переменными и их адресами, я переписал процедуру теста Мейсона следующим образом:

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.Может быть, нам следует спросить Барри Келли, поскольку он является автором нового RTTI D2010, поэтому тоже должен многое знать о старом ...

0 голосов
/ 09 августа 2010

возможно это связанный список, который находится в смежной памяти:)

...