114 C A P I T O L O
7
Conclusioni e sviluppi futuri
La strategia di verifica basata sul concetto di ipd (immediate post-dominator) tenta di minimizzare i requisiti di memoria attraverso una allocazione dinamica: il grafo di flusso viene pensato diviso in blocchi e vengono mantenute in memoria solo le strutture dati necessarie alla verifica di un sotto-insieme delle istruzioni del metodo, in questo modo la massima memoria richiesta diventa quella necessaria alla verifica del blocco più “costoso”. Ogni blocco non banale del grafo comincia con un salto condizionato e termina con il suo ipd quindi, a differenza della verifica standard, non vengono memorizzati tutti i targets (di salti condizionati e incondizionati), ma vengono solo mantenute informazioni relative agli in-frames (cioè variabili locali e stack prima dell’esecuzione) di ogni target del salto condizionato con cui comincia il blocco corrente e del suo ipd.
Un interessante effetto collaterale dell’algoritmo proposto è di non salvare il frame per la prima istruzione di una subroutine: le subroutines vengono invocate infatti con una istruzione di salto incondizionato (jsr, jump subroutine) e quel frame salvato rende complessa la verifica perché per effettuarla “correttamente” è necessaria una analisi polivariante del flusso di dati (senza una analisi polivariante viene spesso rigettato anche codice valido).
Il prototipo sviluppato durante la tesi utilizza il BCEL, un insieme di APIs open-source che offrono una visione object-oriented di un file class binario di Java: in particolare è stato modificato il JustIce, un verificatore polivariante incluso nel package. Sviluppi futuri prevedono una evoluzione del prototipo per il supporto delle subroutines e degli exception handlers.
Durante il lavoro di tesi è stata implementata una ottimizzazione dell’algoritmo di verifica basato sull’ipd: viene evitato il salvataggio di frames duplicati e questo porta ad effetti benefici per lo spazio occupato in memoria e per la velocità del processo di verifica (viene minimizzato il numero di scritture in
CAPITOLO 7:CONCLUSIONI E SVILUPPI FUTURI
115
memoria). Inoltre, dallo studio dell’algoritmo di verifica, sembrerebbe possibile una verifica on-card del bytecode effettuando la data-flow analysis in più passi: ad ogni passo verrebbe verificato un tipo particolare di dati (int, float, ...) e quindi non sarebbe più necessario codificare il contenuto di variabili locali e stack perché il tipo sarebbe implicitamente specificato nel passo (basterebbe un solo bit che indichi la validità di un registro per lo specifico passo della data-flow analysis). L’approccio si basa sull’osservazione che se un registro (variabile locale o slot dello stack) contenesse un tipo di dato non valido, questo sarebbe legato al fallimento di una operazione di lettura/scrittura relativa almeno ad un tipo di dati particolare, quindi verrebbe sicuramente individuato in uno degli passi ‘tipati’ della data-flow analysis. L’algoritmo sembrerebbe apparentemente più lento: bisogna anche considerare però che verrebbero scritti e letti bit, questa volta, quindi tutte le operazioni sarebbero di gran lunga più veloci.