2010-08-09 8 views
7

Se ci si trova abbastanza all'interno degli interni di Delphi, troverete qualcosa di strano e apparentemente non documentato sui record TTypeInfo generati dal compilatore. Se i punti PTypeInfo a un record TTypeInfo all'indirizzo X, a X - 4 troverete i prossimi 4 byte descrivono un puntatore a X. Per esempio:Qual è il "puntatore identità" prima di un TTypeInfo per?

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; 

passare qualsiasi legittima PTypeInfo generato dal compilatore in questa routine, e produrrà lo stesso indirizzo due volte. Ho girato un po 'in TypInfo.pas, ma non vedo nulla che menzioni questo "puntatore identità" o che cosa sia lì per. Qualcuno sa perché questo è lì? Questo sembra essere vero in ogni versione di Delphi da almeno D3 a D2010.

+1

Immagino che manchi un 'var' nel codice sopra ... –

+0

@Andreas: Dove? Non vedo alcun vars mancante ... –

+0

Oops! OK, risolto ora. –

risposta

10

È molto semplice: pacchetti e collegamenti dinamici.

BPL sono DLL. Le DLL sono collegate attraverso le tabelle che vengono applicate alle patch, piuttosto che tutto il codice nel collegamento EXE o DLL rispetto alla DLL che viene riparata (il che farebbe molto male alla condivisione della memoria di sola lettura tra più processi). Per evitare la necessità di un riferimento a TypeInfo(SomeType) da qualche parte nel codice o digitareinfo di un file EXE o DLL, modificato durante il collegamento alla BPL, esiste invece un riferimento indiretto tramite la tabella di importazione.

E 'facile vedere la differenza quando si collegano staticamente contro collegando contro una BPL in questo programma:

{$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. 

sulla mia macchina locale, compilato con dcc32 test.pas, emette:

TObject 
x 00401B64 
x^ 00401B68 

Ma quando compilato con il pacchetto RTL con dcc32 -LUrtl test.pas, emette:

TObject 
x 004051F0 
x^ 40001DA4 

Speriamo che questo chiarisca.

+0

Stavo solo andando a formulare un'idea abbastanza simile, grazie per l'intuizione –

+0

Quindi quel puntatore deve essere da qualche parte per il linker per fare il suo lavoro, e appena viene piazzato immediatamente prima dei dati a cui punta, quando quel dato è nello stesso modulo, per convenzione? OK, questo ha senso. Grazie –

+2

@Mason tutti i fix di tipoinfo - puntatori da un blob di tipoinfo a un altro - sono di tipo PPTypeInfo, non PTypeInfo, per gestire il caso di collegamento dinamico Nel caso del collegamento statico, è necessario che ci sia un puntatore intermedio affinché la convenzione funzioni, e parte del tipo stesso ha lo stesso significato di qualsiasi altro, vale a dire che non è lì per il linker: è lì a causa di la convenzione, e la convenzione è lì a causa del collegamento dinamico, che è fatto per massimizzare il potenziale per la condivisione delle pagine. –

0

forse è una lista collegata che sembra essere in memoria contigua :)

+0

No, perché non c'è nulla che assomigli a un "puntatore successivo" nelle strutture TTypeInfo/TTypeData. Idea interessante, però. –

1

Non capisco tutto quello che sta succedendo, ma quando si ha uno sguardo ad esempio IsPublishedProp nell'unità TypInfo, si vedere che proietta l'ClassInfo dell'istanza come un puntatore ad una struttura TypeInfo:

PTypeInfo(Instance.ClassInfo) 

Quando si guarda il metodo ClassInfo, restituisce un semplice puntatore il cui valore sembra legato al tavolo VMT:

Result := PPointer(Integer(Self) + vmtTypeInfo)^; 

vmtTypeInfo ha un valore di -72. Quattro byte prima di quello a -76 è vmtInitTable. vmtTypeInfo è seguito da FieldTable, MethodTable, DynamicTable ecc

il valore vmtInitTable viene utilizzato per esempio TObject.CleanupInstance e passato al _FinalizeRecord come puntatore alla struttura TypeInfo.

Quindi i quattro byte prima della struttura TypeInfo che punta alla struttura TypeInfo sembrano essere lì per progettazione e parte della struttura vmt.

Modifica

Come Mason ha giustamente sottolineato quanto sopra è una falsa pista completa (vedi commenti). Lascio la risposta in modo che gli altri non debbano inseguirla.

Aggiornamento Per evitare confusione su variabili ed i loro indirizzi, ho riscritto procedura di prova di Mason come segue:

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; 

e chiamarlo con le seguenti informazioni:

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; 

La prima tre producono i risultati descritti da Mason. Ho aggiunto solo un writeln aggiuntivo per mostrare il valore del puntatore per l'ultimo writeln. L'ultima chiamata in TryRTTistuff mostra che quando non si passa un puntatore a una struttura TypeInfo valida, non si ottiene lo stesso valore sul primo e sul terzo writeln della chiamata.

Nessun indizio su cosa sta succedendo con TypeInfo. Forse dovremmo chiedere a Barry Kelly che è l'autore del nuovo D2010 RTTI quindi dovrei sapere molto anche su quello vecchio ...

+0

Scusa, ma stai guardando la cosa sbagliata. TypeInfo non è correlato ai VMT, poiché può esistere anche per i tipi non object. Il VMT contiene un puntatore al record TTypeInfo della classe, ma non è quello che sto chiedendo. –

+0

@Mason: Potresti avere perfettamente ragione, ma ho pensato che "RTTI vecchio stile" supporta solo TypeInfo su classi ed enumerazioni? E quest'ultimo solo se non si assegnano valori specifici nella dichiarazione di enumerazione. RTTI per altri tipi non è apparso fino alla D2010, penso (sto usando D2009). –

+0

No. Le informazioni sul tipo dovevano essere disponibili per qualsiasi tipo di base che potesse essere utilizzato in un DFM, poiché questo è stato inventato dal sistema: serializzazione e deserializzazione del modulo. –