Для чего нужен «указатель идентичности» перед TTypeInfo?

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

8
задан Mason Wheeler 9 August 2010 в 20:00
поделиться

3 ответа

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

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

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

11
ответ дан 5 December 2019 в 13:59
поделиться

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

1
ответ дан 5 December 2019 в 13:59
поделиться

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

0
ответ дан 5 December 2019 в 13:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: