• Non ci sono risultati.

Parte I Lo stato dell’arte

5.2 Metadata e Reflection

Introdotto il concetto di meta-programmazione, è importante per la compresione della presente tesi, esaminare con maggiore dettaglio due concetti alla base della meta-programmazione in .NET: metadata e reflection. Per reflective system si intende un sistema in grado di modificare il proprio stato di esecuzione e la propria rappresentazione. Per far questo deve avere un modello di se stesso e del proprio ambiente d’esecuzione.

5.2.1 Metadata

I metadata forniscono il modello del sistema ovvero le informazioni necessarie a descrivere un programma; sono memorizzati in formato binario, neutrale rispetto al linguaggio, sia in memoria sia in un file eseguibile. I metadata vengono supportati direttamente in maniera standard sin dalla prima versione del CLR. Indipendentemente dal linguaggio utilizzato con la compilazione viene prodotto un managed module (vedi paragrafo 5.3.1): un file eseguibile standard, PE27 che richiede il CLR per poter essere eseguito. Il managed module è formato da 4 parti [2]:

• PE header simile al COFF28, indica il tipo del file;

• CLR header, contiene le informazioni che caratterizzano un modulo come managed module; • Metadata, insieme di tabelle tra cui quelle che descrivono i tipi (e relativi membri) definiti e

riferiti nel codice utente (in linguaggio intermedio IL),

• Codice IL prodotto a tempo di compilazione che il runtime compilerà in istruzioni specifiche (native) della CPU

Ogni tipo di dato, con i relativi membri, definito e/o riferito in un modulo o assembly viene descritto in un metadata. In fase di esecuzionie il runtime carica i metadata in memoria per ottenere informazioni quali:

• descrizione di un assembly: o identità

o tipi esportati

o gli assembly da cui dipende quello considerato o informazioni di sicurezza, permessi di esecuzione • descrizione dei tipi di dato

27

Portable Executable 28

77

o nome, scope, classe base e interfacce implementate o membri (metodi, campi, proprietà, eventi e tipi annidati) • attributi (elementi descrittivi aggiuntivi)

Per esempio i metada relativi ai metodi di una classe sono memorizzati in una tabella MethodDef [5] che riporta le suguenti informazioni:

RVA, Relative Virtual Address, utilizzato dal runtime per calcolare l’indirizzo di memoria iniziale del codice MSIL del metodo;

ImplFlags e Flags contengono maschere di bit che descrivono il metodo (ad esempio indicano se si tratta di un costruttore);

Name è il nome del metodo;

Signature indice nel blob heap della definizione della signature del metodo;

Row RVA ImpFlags Flags Name Signature 1 0x00002050 IL Managed Public Reuse Slot SpecialName RTSSpecialName .ctor .ctor (constructor) 2 0x0000208c IL Managed Public Reuse Slot generate object Tabella 5.1, esempio della tabella MethodDef che fornisce informazioni

sul costruttore e sul metodo generate di una classe

Per ulteriori dettagli sulle tabelle dei metadati e sul formato del PE si rimanda a [6,7,5,2]. I linguaggi supportati da .NET utilizzano metadati per descrivere se stessi in maniera automatica e trasparente allo sviluppatore.

I vantaggi introdotti dai metadati sono:

i moduli e gli assembly sono self-describing ovvero memorizzano tutte le informazioni necessarie per interagire con altri moduli; ogni compilatore CLR produce allo stesso tempo codice IL e metadata che inserisce nello stesso file, rendendo impossibile una mancanza di sincronizzazione tra i due. Con i metadata si ha, quindi, un modello di programmazione più semplice perché consentono di eliminare l’utilizzo di file IDL29 e file header per riferire componenti esterni;

interoperabilità tra i linguaggi e semplificazione della progettazione basata sui componenti in quanto mediante le informazioni nei metadata in un programma si può estendere una classe presente in un altro modulo (riferito) e scritta in un altro linguaggio;

29

78

• maggior controllo sul comportamento a runtime dei programmi, mediante speciali metadata, gli attributi;

I metadata vengono, ad esempio, impiegati dal Garbage Collector per determinare quali oggetti sono raggiungibili e quali possono essere riferiti da quale oggetto; dal processo di verifica del CLR per garantire che il codice utente esegua solo operazioni sicure.

5.2.2 Reflection

Per reflection si intende la capacità di un programma di accedere al proprio stato interno d’esecuzione e possibilmente di modificarlo: è quindi un concetto base dei sistemi auto-adattivi. Due sono gli aspetti fondamentali legati alla reflection:

introspezione, il sistema osserva e decide in base al proprio stato;

intercessione, il sistema modifica la propria esecuzione o altera la propria interpretazione. Il CLR supporta entrambi gli aspetti dando al programmatore la possibilità di individuare i membri, invocare metodi, accedere agli attributi dei tipi a tempo d’esecuzione, di creare istanze di classi il cui nome non è noto fino a runtime. In un reflective system quando il codice sorgente di un programma viene compilato le informazioni relative alla sua struttura vengono preservate sotto forma di metadata nel codice prodotto di più basso livello. Come visto nel paragrafo 5.2.1mediante i metadata, a runtime, si può modificare la sequenza delle operazioni da eseguire. Il CLR espone il suo reflective system attraverso le classi contenute nel namespace System.Reflection. Tutti i tipi sono self-describing e le loro definizioni possono essere accedute tramite il metodo GetType ereditato da System.Object. Il CLR consente anche la scrittura di definizioni di tipo (per informazioni a riguardo si consulti System.Reflection.Emit su [5]). Il loader del CLR gestisce gli application domain (AppDomain), in particolare il caricamento di ogni assembly nell’application domain corretto, e provvede al controllo del layout di memoria della gerarchia dei tipi in ogni assembly. A titolo d’esempio se si vogliono ottenere a tempo d’esecuzione i metodi definiti in una classe (MyType) basta invocare il metodo GetMethods come segue:

MethodInfo[] mi = typeof(MyType).GetMethods(

BindingFlags.Public |

BindingFlags.DeclaredOnly | BindingFlags.Instance);

come parametro viene richiesta una bitmask che permette di definire come filtrare i metodi trovati.

Documenti correlati