SlideShare a Scribd company logo
1 of 46
Download to read offline
`
UNIVERSITA DEGLI STUDI DI TRIESTE
`
FACOLTA DI INGEGNERIA
Corso di Laurea Triennale in Ingegneria dell’Informazione
Tesi di Laurea in
Reti di Calcolatori

Progetto e realizzazione di uno
strumento per la modifica
sistematica di codice JavaScript
finalizzato al testing

LAUREANDO

RELATORE

Dennis Morello

prof. Alberto Bartoli

CORRELATORI

prof. Eric Medvet
dott. Andrea De Lorenzo

Anno Accademico 2012/2013
Ai miei cari
Indice

1 Introduzione

1

2 Scenario
2.1 Scopo del progetto . . . . . . . .
2.2 Language parsing . . . . . . . . .
2.2.1 Grammatiche . . . . . . .
2.3 ANTLR . . . . . . . . . . . . . .
2.3.1 Cos’` ANTLR? . . . . . .
e
2.3.2 Grammatiche ANTLR . .
2.3.2.1 Lexer Grammar
2.3.2.2 Parser Grammar
2.3.2.3 Tree Grammar .
2.4 Abstract Syntax Tree . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

3
3
3
4
5
5
6
7
8
8
9

3 Progettazione
3.1 Specifiche del problema . . . . . .
3.2 Manipolazione di codice sorgente .
3.2.1 Da codice sorgente ad AST
3.2.2 Manipolazione di AST . . .
3.2.3 Da AST a codice sorgente .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

11
11
11
11
13
15

4 Implementazione
4.1 Impiego di ANTLR . . . . . . . . . . . . . . .
4.1.1 ANTLRWorks . . . . . . . . . . . . .
4.2 Ordinamento di un vettore . . . . . . . . . .
4.2.1 BubbleSort . . . . . . . . . . . . . . .
4.2.1.1 Pseudo-codice di BubbleSort
4.3 Prove eseguite . . . . . . . . . . . . . . . . . .
4.3.1 Generazione casuale di vettori . . . . .
4.3.2 Ordinamento con BubbleSort originale

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

17
17
17
18
19
19
20
21
23

v
vi

INDICE

4.3.3
4.3.4
4.3.5

Modifica di BubbleSort . . . . . . . . . . . . . . .
Ordinamento con BubbleSort modificato . . . . . .
Indici di prestazione . . . . . . . . . . . . . . . . .

5 Risultati
5.1 Situazioni possibili . . . . . . . . .
5.1.1 Ordinamento corretto . . .
5.1.2 Ordinamento con mutazione
5.2 Analisi dei risultati . . . . . . . . .
5.2.1 1000 mutazioni . . . . . . .
5.2.2 5000 mutazioni . . . . . . .
5.2.3 Confronto tra i risultati . .

. . . . . . . . . .
. . . . . . . . . .
non equivalente
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

24
24
27
31
31
31
31
32
33
34
36

6 Conclusioni
37
6.1 Sviluppi futuri . . . . . . . . . . . . . . . . . . . . . . . . 37
7 Ringraziamenti

39
Capitolo

1

Introduzione
Modificare in modo automatico e sistematico il codice sorgente di un
programma pu` essere utile in varie applicazioni, quali ad esempio la
o
correzione automatica di bug.
Un modo per raggiungere questo scopo ` quello che prevede un ape
proccio evoluzionistico: si generano tante mutazioni casuali al codice
sorgente originale e, per ognuna di esse, si valutano i risultati e si sceglie
la soluzione migliore.
A tal fine, ` necessario mettere a punto uno strumento che autonomae
mente esegua modifiche al codice, che si occupi di eseguirlo e che analizzi
i risultati conseguiti, confrontandoli con quelli attesi.
In questa tesi ` stato progettato e sviluppato uno strumento utile
e
in questo ambito, in particolare, uno strumento in grado di misurare
l’impatto che una modifica al codice sorgente di un programma produce
sul suo output, nel senso che verr` illustrato dettagliatamente nei capitoli
a
successivi.

1
Capitolo

2

Scenario
Verranno affrontati in questo capitolo alcuni argomenti preliminari e
verr` chiarito lo scopo del lavoro di tesi.
a

2.1

Scopo del progetto

Lo scopo di questo lavoro ` quello di determinare le conseguenze proe
dotte da mutazioni successive nella struttura del codice sorgente di una
funzione JavaScript.

2.2

Language parsing

Prima di affrontare l’argomento che d` il nome a questo paragrafo, `
a
e
necessario introdurre alcuni concetti.
Un linguaggio naturale si definisce a partire da un alfabeto; con i suoi
simboli ` possibile formare un insieme di sequenze, dette parole, ma non
e
tutte le sequenze sono parole del linguaggio naturale.
La grammatica del linguaggio fornisce le regole per decidere quali
sequenze sono parole del linguaggio.
Le parole del linguaggio naturale possono essere raggruppate in insiemi detti frasi, ma anche qui non tutte le frasi appartengono al linguaggio.
La sintassi del linguaggio fornisce le regole per decidere quali sequenze sono frasi del linguaggio (si parla di frasi sintatticamente corrette).
Un linguaggio di programmazione ` un linguaggio artificiale; per poe
terlo definire ` necessario dare ancora qualche definizione.
e
Dato un insieme finito non vuoto V (chiamato solitamente alfabeto),
si definisce universo linguistico su V , e si indica con V ∗, l’insieme delle
sequenze finite di lunghezza arbitraria di elementi di V .
Un linguaggio L sull’alfabeto V ` allora un sottoinsieme di V ∗.
e
3
4

2. Scenario

Nel considerare i linguaggi di programmazione, non siamo interessati
a tutti i possibili sottoinsiemi di V ∗, ma solo a quelli definibili in maniera
finita; questa descrizione pu` essere, per esempio, fornita attraverso una
o
grammatica (si veda il paragrafo successivo).
Una volta definita la grammatica, per language parsing si intende il
processo di analisi di stringhe di simboli, in accordo con quanto sancito
dalla grammatica.
Qui non si intende risalire al significato di una sequenza di caratteri,
ma semplicemente riconoscere ed isolare alcune sequenze (che in ambito
operativo vengono chiamate token).

2.2.1

Grammatiche

In informatica, una grammatica G ` definita da
e
• Un alfabeto V di simboli terminali
• Un alfabeto N di simboli non terminali
• Un assioma S ∈ N tale che V ∩ N = ∅
• Un insieme finito di regole sintattiche P del tipo X → α, dove
X ∈ N e α ∈ (N ∪ V )∗ e si legge “X produce α”
Se in una grammatica vi sono pi` regole aventi la stessa parte sinistra,
u
ad esempio
X → α1

X → α2

···

X → αn

allora esse sono raggruppate usando la convenzione notazionale
X → α1 | α1 | · · · | αn
La descrizione della grammatica di un linguaggio di programmazione
avviene per mezzo di un metalinguaggio formale1 che prende il nome
di BNF (Backus-Naur Form, dai nomi dei due studiosi che per primi
l’hanno introdotta negli anni ’50) e sue varianti.
Il formalismo BNF viene spesso usato non nella sua forma originale,
ma utilizzando alcune estensioni che permettono una scrittura pi` concisa
u
delle grammatiche; si parla allora di EBN F (Extended BNF - non ci si
addentrer` nei dettagli, per maggiori informazioni si rimanda all’indirizzo
a
http://www.cs.cmu.edu/∼pattis/misc/ebnf.pdf).
1

Un metalinguaggio ` un linguaggio usato per parlare di un altro linguaggio.
e
5

2.3
2.3.1

ANTLR

ANTLR
Cos’` ANTLR?
e

ANTLR2 (ANother Tool for Language Recognition) ` uno strumento
e
software tipicamente impiegato nella definizione di linguaggi formali e
nella costruzione di strumenti che operino su di essi, come traduttori,
compilatori o analizzatori di codice sorgente; permette, tra le altre cose,
di generare parser di testo e Abstract Syntax Trees (AST) per mezzo
della definizione di grammatiche.
Una grammatica ANTLR ` un file di testo contenente una successione
e
di regole che stabiliscano come interpretare il flusso di byte da analizzare
(per maggiori dettagli vedasi il paragrafo 2.3.2).
Per ogni file di grammatica, ANTLR genera alcuni file sorgenti Java
(ma ` possibile, mediante l’inserimento dell’opzione language=x all’intere
no della grammatica, generare file sorgenti nello specifico linguaggio x; la
lista dei linguaggi supportati da ANTLR 3 ` disponibile all’indirizzo http:
e
//www.antlr.org/wiki/display/ANTLR3/Code+Generation+Targets/) che verranno poi utilizzati dall’applicazione sviluppata.
I file sorgenti generati da ANTLR fanno riferimento a strumenti che
riconoscano, analizzino e trasformino dati di input la cui struttura sia
stata meticolosamente descritta nella grammatica; tali strumenti sono
sostanzialmente i seguenti:
Lexer
Viene generato a partire dal file contenente la lexer grammar; legge
una sequenza di byte (siano essi caratteri o dati binari), li divide
in token secondo i pattern specificati (si veda il paragrafo 2.3.2.1)
e genera una sequenza di token come output; alcuni di essi (come
spazi vuoti o caratteri di punteggiatura) possono essere ignorati ed
omessi dall’output.
Parser
Viene generato a partire dal file contenente la parser grammar;
legge una sequenza di token generata dal lexer e li raggruppa in
frasi del linguaggio considerato secondo le regole specificate (par.
2.3.2.2); pu` inoltre eseguire alcune azioni aggiuntive, come raffinao
re le frasi prodotte, emettere del testo a seconda dei token incontrati
(tipico comportamento di un traduttore) o generare un AST che
venga poi manipolato per modificare la struttura del linguaggio (`
e
questo l’uso di ANTLR che ` stato fatto nel presente lavoro di tesi).
e
Tree Parser
Viene generato a partire dal file contenente la tree grammar; visita
2

http://www.antlr.org/
2. Scenario

6

l’albero generato dal parser e, per ogni nodo incontrato, esegue le
operazioni eventualmente descritte nella relativa grammatica (par.
2.3.2.3). Tali operazioni possono essere principalmente output di
testo o manipolazione di altri nodi.

2.3.2

Grammatiche ANTLR

Come accennato nei paragrafi 2.2.1 e 2.3.1, una grammatica ANTLR
` una sequenza di regole che descrivono ciascuna un sottoinsieme della
e
sintassi del linguaggio; assieme ad esse, una grammatica contiene il suo
tipo (lexer grammar o tree grammar ), il suo nome, alcune opzioni, ed
eventualmente altre sezioni come la specifica di token, attributi e azioni:
grammarType grammar name;
<<options>>
<<tokens>>
<<attributes>>
<<actions>>
rule1 : ... | ... | ... ;
...
ruleN : ... | ... | ... ;
La sintassi con cui vengono specificate le regole in ANTLR riprende la
notazione Extended Backus-Naur Form (EBNF, si veda paragrafo 2.2.1).
In analogia a quanto avviene in Java nella corrispondenza tra il nome
di una classe e quello del file che la contiene, in ANTLR il nome della
grammatica deve essere uguale al nome del file in cui ` memorizzata; i
e
file di grammatica hanno usualmente estensione g.
La regola da cui inizia il parsing ` chiamata regola di partenza (ogni
e
regola pu` esserlo); ogni regola consiste a sua volta in una o pi` alternao
u
tive, che altro non sono che sotto-regole; l’ordine con cui si susseguono `
e
ininfluente.
A titolo di esempio, una regola chiamata variableDefinition potrebbe avere due alternative, la prima per una semplice definizione di
variabile (ad esempio var x;) e la seconda per una definizione con inizializzazione (var x=12;):
variableDefinition
: declaration
| declarationWithInitialization
;
Da una generica grammatica T contenuta nel file T.g (supponendo
che il linguaggio di programmazione scelto per lo sviluppo del progetto
7

ANTLR

sia Java), ANTLR genera i file sorgenti TLexer.java e TParser.java i
quali contengono, rispettivamente, il lexer ed il parser per il proprio linguaggio; se sono stati specificati dei token immaginari (ridenominazione
di sequenze di caratteri utilizzabili all’interno della grammatica, come ad
esempio EQUALS=’=’;), essi vengono memorizzati nel file T.tokens.
Nei paragrafi seguenti si analizzeranno le tre tipologie di grammatiche
definibili in ANTLR.
2.3.2.1

Lexer Grammar

Scopo della lexer grammar ` quello di definire come debbano essere coe
struiti i token a partire dall’analisi in prima battuta dell’input assegnato.
Un token `, nell’accezione tipica, un insieme di caratteri che rispetti
e
una particolare regola definita nella lexer grammar. Ad esempio, volendo riconoscere tutti i numeri interi senza segno di lunghezza arbitraria
che abbiano come prima cifra un carattere diverso dallo zero, potremmo
definire la regola seguente:
INT : (’1’..’9’)(’0’..’9’)* ;
dove il carattere * indica che il gruppo (’0’..’9’) pu` comparire zero
o
o pi` volte.
u
Una volta definite le regole3 per il riconoscimento dei token desiderati,
ogni successione di caratteri che abbia senso per la lexer grammar dar`
a
origine ad un token; se tra le regole definite vi fosse la regola INT descritta
nell’esempio sopra, la sequenza di caratteri 0x032C produrrebbe il token
32.
Fatte queste premesse, ` possibile affermare che la lexer grammar `
e
e
una sequenza di modi di riconoscere i token d’interesse:
grammar T;
...
RULE_1 : ... | ... | ... ;
...
RULE_N : ... | ... | ... ;
La costruzione dei token ` indispensabile per il passaggio successivo,
e
quello che vede come protagonista il parser.
Terminata la stesura di questa grammatica, ANTLR genera il file TLexer.java, che potr` poi essere utilizzato nel progetto che si sta
a
sviluppando (T ` il nome della grammatica dell’esempio).
e
3

Per convenzione, i nomi delle regole della lexer grammar sono tutte maiuscole.
8

2. Scenario

2.3.2.2

Parser Grammar

Una parser grammar ` un insieme di regole necessarie al parser per rie
conoscere particolari sequenze di token generate dal lexer; ad esempio,
` possibile riconoscere lo statement return di JavaScript specificando la
e
regola
returnStatement : ’return’ expr ’;’ ;
dove expr ` un’opportuna regola.
e
Valgono le stesse considerazioni fatte per la lexer grammar relativamente alle alternative di una regola.
Specificando l’opzione output=AST nella sezione options della grammatica, ` possibile generare l’AST associato al particolare input; per
e
generare un nodo dell’AST per la regola returnStatement ` sufficiente
e
modificarne la definizione come segue:
returnStatement
: ’return’ expr ’;’ -> ^(’return’ expr)
;
Tale regola genera un nodo che ha per padre la stringa "return" e per
figlio il sotto-albero expr.
Per i dettagli su come vengano definiti gli AST in ANTLR si veda il
paragrafo ad essi dedicato.
Da notare che lexer grammar e parser grammar sono di norma contenute in un unico file.
Al termine della stesura della parser grammar, ` possibile utilizzare
e
ANTLR per generare il file TParser.java.
2.3.2.3

Tree Grammar

La tree grammar ` un file contenente una sequenza di regole necessarie
e
a riconoscere una determinata struttura di un AST.
In particolare, ciascuna regola specifica la conformazione di un sottoalbero costituito da un padre e uno o pi` figli (i quali possono essere a
u
loro volta altri padri o foglie).
Ad esempio, la regola seguente riconosce il sotto-albero che rappresenta lo statement return:
returnNode
: ^(’return’ expr)
;
`
E poi possibile eseguire operazioni specifiche in corrispondenza di un
nodo che rispetti una determinata regola (ad esempio, emettere del testo
9

Abstract Syntax Tree

come output) inserendo dopo la definizione di un’alternativa un blocco di
codice Java (o comunque nel linguaggio target) racchiuso tra partentesi
graffe:
returnNode
: ^(’return’ expr) { System.out.println("Nodo return"); }
;
Questa regola, ogni qualvolta si incontri un nodo return, produrr` una
a
stampa a video della stringa "Nodo return".
Una volta scritte e combinate tra loro tutte le regole necessarie, se il
file contenente la grammatica si chiamasse TWalker.g, ANTLR produrrebbe il file TWalker.java, necessario al riconoscimento effettivo degli
AST la cui struttura ` conforme alla grammatica; questo file rappresenta
e
quello che in precedenza ` stato chiamato tree parser.
e

2.4

Abstract Syntax Tree

Un Abstract Syntax Tree ` una rappresentazione ad albero della struttura
e
sintattica di un codice sorgente scritto in un particolare linguaggio di
programmazione.
Ogni nodo dell’albero denota un costrutto presente nel codice sorgente, sebbene non ne rappresenti fedelmente la sintassi, ma solo le parti
essenziali. Ad esempio, le parentesi graffe che racchiudono un blocco di
codice nei linguaggi C-like sono implicite in un AST, e non vi vengono
rappresentate.
A titolo esemplificativo, sia dato il seguente codice sorgente JavaScript:
1
2
3
4
5
6
7
8
9

function prova (a , b ) {
var c ;
if ( a +b >9) {
c =2;
} else {
c =0;
}
return c ;
}

Il corrispondente AST, generato a partire dalla grammatica TinyJavaScript,
` quello riportato in figura 2.1.
e
2. Scenario

10

Figura 2.1: AST corrispondente al codice JavaScript dell’esempio.

Il nodo nil rappresenta la radice4 dell’albero, il nodo SLIST denota
una successione di statement; tutti gli altri nodi hanno un significato
immediato.

4

Secondo la convenzione adottata da ANTLR.
Capitolo

3

Progettazione
La fase progettuale mette in chiaro come si sia scelto di affrontare il
passaggio da un codice JavaScript ad un altro che differisca dal primo
solo per una singola modifica, nel senso che verr` definito pi` avanti.
a
u

3.1

Specifiche del problema

Il problema affrontato prevede di analizzare una singola funzione JavaScript (costituita da un insieme di statement di numerosit` arbitraria)
a
e di scambiare le posizioni di due parti di codice scelte “a caso”; nei
paragrafi successivi si tratteranno approfonditamente i dettagli di queste
operazioni.

3.2

Manipolazione di codice sorgente

Le manipolazioni nel codice sorgente che verranno qui considerate consistono in scambi casuali di nodi dell’AST corrispondente.
Per fare ci` ` necessario prima analizzare il codice, costruirne il reoe
lativo AST, eseguire gli scambi e visitare l’AST mutato per generare il
nuovo codice sorgente.

3.2.1

Da codice sorgente ad AST

Il passaggio da codice sorgente ad AST richiede come prerequisito la
stesura della grammatica necessaria al riconoscimento dei costrutti JavaScript; a tale scopo ` stata scritta la grammatica TinyJavaScript nel file
e
TinyJavaScript.g utilizzando ANTLRWorks (par. 4.1.1); analizziamo
brevemente il suo contenuto.
11
12

3. Progettazione

La prima sezione, denominata options, contiene dei settaggi che
ANTLR utilizzer` per generare il lexer ed il parser (par. 2.3.1):
a
1
2
3
4
5

options {
output = AST ;
ASTLabelType = CommonTree ;
backtrack = true ;
}

In particolare, l’opzione output=AST; dice ad ANTLR che l’output del
parser sia l’AST corrispondente al codice sorgente ispezionato.
Segue poi la definizione di alcuni token immaginari (par. 2.3.2) che
verranno impiegati all’interno della grammatica:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

tokens {
VAR ;
FUNC ;
ARG ;
NEW ;
CALL ;
INDEX ;
SLIST ;
FOR ;
COND ;
ITERATE ;
WHILE_DO ;
DO_WHILE ;
IF ;
THEN ;
ELSE ;
SWITCH ;
DEFAULT ;
RETURN ;
}

//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//

variable definition
function definition
formal argument
new object
function call
array index
statement list
for cycle
condition in for cycle
iteration in for cycle
while cycle
do .. while cycle
if statement
then block
else block
switch statement
switch default case
return statement

TinyJavaScript.g contiene sia le regole del lexer (normalmente denotate con lettere maiuscole) sia quelle del parser.
Prendiamo in esame una delle regole del lexer:
1
2

3

ID
: ( ’a ’.. ’ z ’| ’A ’.. ’ Z ’| ’_ ’) ( ’a ’.. ’ z ’| ’A ’.. ’ Z
’| ’0 ’.. ’9 ’| ’ _ ’) *
;

Questa regola consente di riconoscere tutte le stringhe di caratteri che
inizino con una lettera minuscola o maiuscola oppure col carattere under-
13

Manipolazione di codice sorgente

score e a cui seguono, eventualmente (l’asterisco al termine del secondo
gruppo significa appunto che quel gruppo ` opzionale), altre lettere mae
iuscole o minuscole oppure numeri oppure caratteri underscore. Qualsiasi altro carattere (punteggiatura, parentesi, ...) non viene preso in
considerazione da questa regola.
Una regola del parser ` invece ad esempio questa:
e
1
2

3

4

ifStat
: ’if ’ ’( ’ cond = condStat ’) ’ mthen = stat ( ’ else ’
melse = stat ) ?
-> ^( IF ^( COND $cond ) ^( THEN $mthen ) ^( ELSE $melse
?) )
;

Si tratta, com’` intuibile, della regola per il riconoscimento di costrutti
e
if-then-else, con l’accorgimento che il ramo else pu` essere assente.
o
I caratteri “->” indicano che l’AST generato deve avere la struttura
specificata come segue: il nodo padre ` IF seguito dai figli COND, THEN e
e
ELSE. Ciascuno di essi ` a loro volta padre di altri sotto-alberi, eccetto
e
il nodo ELSE, il quale pu` non avere figli se nel corrispondente codice
o
JavaScript non ` presente il blocco else.
e
Il parsing del codice inizia dalla regola program mediante le istruzioni

1

2
3
4

5

6

ANTLRFileStream stream = new ANTLRFileStream ( "
BubbleSort . js " ) ;
lexer = new T i n yJ a v aS c r ip t L ex e r ( stream ) ;
tokens = new Co mmonTo kenStr eam ( lexer ) ;
T i n y J a v a S c r i p t P a r s e r parser = new T i n y J a v a S c r i p t P a r s e r
( tokens ) ;
T i n y J a v a S c r i p t P a r s e r . program_return r = parser . program
() ;
tree = ( CommonTree ) r . getTree () ;

Una volta terminato il parsing, la variabile tree contiene l’AST
generato.

3.2.2

Manipolazione di AST

La manipolazione dell’AST inizia specificando quanti scambi di nodi
devono essere eseguiti; supponiamo siano in numero n.
Si esegue dunque un ciclo for avente n iterazioni; ad ogni iterazione
si compiono le operazioni seguenti:
3. Progettazione

14

• Si inizia un ciclo while, eseguito fintantoch´ non venga prodotta
e
una mutazione valida; ogni iterazione di questo ciclo prevede le
seguenti istruzioni:
– Vengono generati due numeri casuali interi non negativi k1
e k2 che assumono come valore minimo 1 e massimo p, con
p numero totale di sotto-alberi; tali numeri costituiscono gli
indici dei nodi che verranno selezionati
– Si copia l’AST attuale per consentirne il ripristino in caso di
scambio non valido
– Si esegue lo scambio del nodo k1 col nodo k2
– Si verifica la legittimit` dello scambio; se lo ` si esce dal
a
e
ciclo while e si procede con la successiva iterazione del ciclo for esterno, altrimenti si ripristina l’AST alla situazione
precedente allo scambio e si ripetono i passi del ciclo while
• A mutazione avvenuta, si visita l’AST per generarne il codice sorgente JavaScript (paragrafo successivo) e viene scritto il risultato
su un file avente nome ModBubble*.js, con * numero progressivo
che va da 0 a n − 1
• Al termine del ciclo for la variabile tree contiene la versione
mutata della funzione JavaScript originale
Il codice Java che implementa quanto descritto `
e
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17

CommonTree sree ;
int index = 0;
for ( int i =0; i <n ; i ++) {
boolean changed = false ;
while (! changed ) {
count = 0;
trees . clear () ;
visitTree ( tree ) ;
k1 = random . nextInt ( count ) ;
k2 = random . nextInt ( count ) ;
sree = copyTree ( tree ) ;
swapNodes ( trees . get ( k1 ) , trees . get ( k2 ) ) ;
changed = ( sree . toStringTree () . equals ( tree .
toStringTree () ) ? false : true ) ;
}
writeCodeToFile ( " ModBubble " + index + " . js " ) ;
index ++;
}
15

Manipolazione di codice sorgente

Si noti che la presenza del ciclo while assicuri che vengano eseguiti
esattamente n scambi.
La funzione visitTree(t) ispeziona l’AST per calcolarne il numero totale di sotto-alberi e, per completezza, li inserisce nell’ArrayList
chiamato trees (definito all’inizio della classe):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public static void visitTree ( CommonTree t ) {
if ( t . getChildCount () > 0) {
for ( int i =0; i <t . getChildCount () ; i ++) {
if ( down ) {
count ++;
trees . add ( t ) ;
}
down = true ;
visitTree (( CommonTree ) t . getChild ( i ) ) ;
}
} else {
count ++;
trees . add ( t ) ;
down = false ;
}
}

Il metodo writeCodeToFile(s) verr` discusso nel paragrafo che sea
gue.

3.2.3

Da AST a codice sorgente

Il passaggio da AST a codice sorgente avviene visitando l’albero utilizzando l’algoritmo Depth-First Search 1 .
Si parte dal nodo denominato program e si ispezionano i nodi figli, da
sinistra verso destra. Ad ogni visita di un nodo si stampa il suo contenuto
e si passa all’ispezione dei figli; al raggiungimento del nodo foglia per quel
sotto-albero, si passa ad ispezionare il nodo fratello successivo a quello
che si era preso in esame.
Si procede in questo modo fino a quando tutti i nodi dell’AST siano
stati ispezionati.
Una volta terminata la generazione del codice JavaScript, si scrive il
risultato su un file il cui nome viene specificato dal parametro del metodo
writeCodeToFile(s).
1
2

public static void writeCodeToFile ( String name ) {
C o de S t ri n g Ge n e ra t o r cg = new C o de S t ri n g Ge n e ra t o r (
tree ) ;
1

http://en.wikipedia.org/wiki/Depth-first search
3. Progettazione

cg . visitTree ( tree ) ;
cg . writeCodeToFile ( name ) ;

3
4
5

16

}

La classe CodeStringGenerator contiene tutti i metodi necessari alla
generazione del codice a partire dall’ispezione dell’AST; tra di essi vi `
e
writeCodeToFile(s), il cui listato ` riportato qui sotto:
e
1
2
3
4
5
6
7
8
9
10
11

public boolean writeCodeToFile ( String fileName ) {
if ( code . length () ==0) visitTree ( tree ) ;
try {
FileWriter fw = new FileWriter ( fileName , false ) ;
fw . write ( code ) ;
fw . close () ;
return true ;
} catch ( Exception e ) {
return false ;
}
}

Come si vede, se la scrittura ` andata a buon fine, il metodo restituisce
e
il valore booleano true, altrimenti restituisce false.
Capitolo

4

Implementazione
4.1

Impiego di ANTLR

Il tipico impiego di ANTLR consiste nelle seguenti tre macro-operazioni:
1. Uso del lexer e del parser in serie per analizzare il flusso di dati in
ingresso, controllarne la conformit` alle grammatiche sviluppate e,
a
se non sono stati incontrati errori, generare una rappresentazione
intermedia come l’AST;
2. Opzionalmente, modificare l’AST per mezzo di uno o pi` Tree
u
Parser (par. 2.3.1);
3. Produrre l’output finale utilizzando un Tree Parser, ad esempio per
generare codice sorgente modificato.

4.1.1

ANTLRWorks

ANTLRWorks (http://www.antlr3.org/works/) ` un IDE per la stesura
e
di grammatiche ANTLR scritto in Java, originariamente sviluppato da
Jean Bovet in collaborazione con il prof. Terence Parr.
Sebbene attualmente sia disponibile la versione 2.1 che supporta pienamente le specifiche di ANTLR 4, si ` qui preferito utilizzare la versioe
ne 1.5 in quanto ` stata specificatamente studiata per ANTLR 3.x (la
e
versione di ANTLR utilizzata nel progetto ` la 3.5).
e
ANTLRWorks fornisce funzionalit` quali evidenziazione della sintasa
si, creazione dell’albero delle regole, ricerca mediante espressioni regolari,
refactoring, suggerimenti in tempo reale, generazione del diagramma decisionale, interprete interattivo per il testing delle regole, visualizzazione
grafica di AST, esportazione su file dei diagrammi generati, debugger e
molto altro ancora.
17
4. Implementazione

18

Seguono alcuni screenshot dell’applicazione:

Figura 4.1: Scrittura di regole ANTLR.

Figura 4.2: Funzionalit` di debugging.
a

4.2

Ordinamento di un vettore

Come algoritmo su cui eseguire le manipolazioni che verranno pi` avanti
u
descritte, ` stato scelto un algoritmo di ordinamento di un vettore; nello
e
specifico, ` stato considerato BubbleSort.
e
19

4.2.1

Ordinamento di un vettore

BubbleSort

L’algoritmo BubbleSort (letteralmente “ordinamento a bolla”) funziona
in modo piuttosto intuitivo: dato un vettore di n numeri, si inizia a
confrontare il primo elemento con il successivo; se il primo risulta essere
maggiore del secondo allora avviene uno scambio di posizione, altrimenti no. Si procede poi confrontando il secondo con il terzo elemento, si
riesegue il confronto ed eventualmente si invertono le posizioni; si ripete
questo procedimento sino al confronto tra il penultimo e l’ultimo elemento (dunque n − 1 volte), dopodich´ si ricomincia dalla prima coppia del
e
vettore. Si continuano ad eseguire confronti lungo tutta la lunghezza del
vettore sino a quando non sono stati eseguiti n(n − 1) confronti totali.
Per meglio spiegarne l’idea, si ricorre ad un esempio: si immagini di
avere in mano n carte da gioco francesi e si supponga di volerle ordinare.
A tale scopo, si confronti la prima carta con la seconda: se la prima `
e
maggiore della seconda allora la prima carta prende il posto della seconda e viceversa, altrimenti non avviene alcuno scambio. Si proceda poi
con la seconda e la terza carta e si ripeta il confronto; l’intero procedimento va ripetuto sino all’ultima carta in mano. A questo punto si
avr` una successione di carte non ancora completamente ordinata; per
a
raggiungere una situazione di ordinamento globale ` necessario rieseguire
e
il procedimento descritto sino a quando la mano di carte risulti ordinata.
Un limite superiore al numero di confronti che ` necessario eseguire per
e
ordinare n carte con questo metodo ` appunto n(n − 1).
e
4.2.1.1

Pseudo-codice di BubbleSort

Si riporta di seguito lo pseudo-codice dell’algoritmo BubbleSort:
bubblesort(v):
u = v
n = length(v)
for i=1 to n do
for j=1 to n-1 do
if u[j]>u[j+1] then
k = u[j]
u[j] = u[j+1]
u[j+1] = k
return u
Come si nota, tale algoritmo non modifica il vettore v originale, ma
produce una sua copia ordinata.
In realt`, nella presente tesi ` stata utilizzata una versione modifia
e
cata della versione elementare di BubbleSort qui esposta: in effetti, non
4. Implementazione

20

sempre ` necessario eseguire n(n − 1) iterazioni prima di giungere all’ore
dinamento completo; se si introduce una variabile sentinella che segnali
l’assenza di scambi in un’iterazione del secondo ciclo for (e dunque si `
e
giunti prematuramente all’ordinamento), ` possibile interrompere i cicli
e
e restituire direttamente il vettore u.
Lo pseudo-codice di questa versione ottimizzata di BubbleSort ` il
e
seguente:
smart_bubblesort(v):
u = v
n = length(v)
for i=1 to n do
flag = false
for j=1 to n-1 do
if u[j]>u[j+1] then
k = u[j]
u[j] = u[j+1]
u[j+1] = k
flag = true
if flag==false then
return u
return u

4.3

Prove eseguite

Il linguaggio di programmazione scelto per l’implementazione di BubbleSort ` JavaScript.
e
Nel file BubbleSort.js ` stato memorizzato il seguente codice:
e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

function bubbleSort ( array ) {
var ordered = copyArray ( array ) ;
for ( var i =0; i < ordered . length ; i ++) {
var flag = false ;
for ( var j =0; j < ordered . length -1; j ++) {
if ( ordered [ j ]) {
var k = ordered [ j ];
ordered [ j ] = ordered [ j +1];
ordered [ j +1] = k ;
flag = true ;
}
}
if (! flag ) {
break ;
}
}
21
return ordered ;

17
18

Prove eseguite

}

`
E questo il file di partenza che viene modificato dal cuore del programma
(par. 3.2.2).
Per interfacciarsi alle funzionalit` richieste ` stata creata una pagina
a
e
HTML divisa in varie sezioni.
La prima sezione contiene un form attraverso cui specificare le caratteristiche dei vettori da generare casualmente; per casualmente si intende
che ciascuno di essi ` costituito da un numero casuale di elementi come
presi tra un valore minimo e uno massimo specificati dall’utente e che
ogni elemento dei vettori generati ` scelto a caso, con il vincolo che non
e
vi siano ripetizioni di elementi all’interno dello stesso vettore.

Figura 4.3: Form di generazione casuale dei vettori.

La seconda sezione consente di ordinare i vettori generati con l’algoritmo BubbleSort originale, ovvero non ancora mutato.
La terza sezione contiene una drop area in cui trascinare i file contenenti le versioni mutate di BubbleSort.
Infine, la quarta ed ultima sezione contiene alcuni grafici relativi alle performance degli algoritmi eseguiti; i dettagli verranno discussi nel
capitolo relativo all’analisi dei risultati.

4.3.1

Generazione casuale di vettori

Il primo step per la valutazione delle prestazioni degli algoritmi prodotti
consiste nella generazione dei vettori su cui poi lavorare.
Una volta specificati il numero di vettori da produrre, il numero minimo e massimo degli elementi di ciascuno di essi e il massimo valore
che ogni elemento pu` assumere (inteso come valore assoluto: specio
ficando 100 come valore massimo, i valori possibili saranno contenuti
4. Implementazione

22

Figura 4.4: Output dei vettori generati.

Figura 4.5: Ordinamento dei vettori con BubbleSort non modificato.

Figura 4.6: Inserimento dei file contenenti versioni mutate di BubbleSort.

nell’intervallo discreto [−100, 100]), la funzione che genera i vettori ` la
e
seguente:
23

1

2
3
4

5
6
7

Prove eseguite

function genRandMatrix ( rows , colsMin , colsMax , valMax )
{
var matrix = new Array ( rows ) ;
for ( var i =0; i < rows ; i ++) {
matrix [ i ] = genRandArray ( colsMin , colsMax , valMax )
;
}
return matrix ;
}

La funzione genRandArray(a,b,c) produce un vettore che ha tra a
e b elementi e ciascuno di essi ` compreso tra -c e +c; ` cos` costituita:
e
e
ı
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

function genRandArray ( colsMin , colsMax , valMax ) {
var l = posRand ( colsMin , colsMax ) ;
var array = new Array ( l ) ;
var rand ;
var present ;
for ( var i =0; i <l ; i ++) {
present = true ;
rand = random ( valMax ) ;
while ( present && i > 0) {
for ( var j =0; j <i ; j ++) {
if ( array [ j ]== rand ) {
rand = random ( valMax ) ;
break ;
} else {
if ( j == i -1) present = false ;
}
}
}
array [ i ] = rand ;
}
return array ;
}

La funzione posRand(a,b) genera un numero intero casuale compreso
tra a e b; il ciclo while e la variabile booleana present sono necessarie
per assicurarsi che non vi siano elementi ripetuti nel vettore generato; la
funzione random(a) genera un numero intero casuale compreso tra -a e
+a.

4.3.2

Ordinamento con BubbleSort originale

Dopo che i vettori siano stati generati, si procede al loro ordinamento con
la versione originale di BubbleSort; si ottiene come risultato una copia
4. Implementazione

24

correttamente ordinata dei vettori di partenza.
Si noti che i vettori originali non vengono eliminati: dovranno infatti
essere ordinati dalle versioni modificate di BubbleSort.

4.3.3

Modifica di BubbleSort

Partendo dal file BubbleSort.js che contiene il codice JavaScript di
BubbleSort originale, si esegue il programma sviluppato per la generazione di n versioni mutate di BubbleSort; il funzionamento di tale
programma ` sintetizzabile nei seguenti passaggi:
e
• Analizza il file BubbleSort.js e dal codice sorgente genera l’AST
(par. 3.2.1)
• Esegue n volte quanto segue:
– Sceglie a caso due nodi dell’albero
– Verifica se lo scambio ` fattibile (i due nodi non devono essere
e
l’uno l’antenato dell’altro e non devono essere lo stesso nodo)
– Esegue un tentativo di scambio; se lo scambio ` andato a
e
buon fine (nel senso che il nuovo albero ` conforme alla tree
e
grammar) allora si procede al prossimo scambio, altrimenti si
ripetono i due passi precedenti fintantoch´ non venga eseguito
e
uno scambio valido (par. 3.2.2)
– L’AST mutato viene ritrasformato in codice JavaScript ed il
risultato viene scritto nel file ModBubble*.js, dove al posto
del carattere * vi ` un numero progressivo compreso tra 0 e
e
n − 1 (par. 3.2.3)
• Al termine del ciclo, tutte le mutazioni sono state scritte su file;
il programma ` stato studiato appositamente per avere la certezza
e
che in ogni caso vi siano esattamente n mutazioni
Il risultato dell’esecuzione del programma appena descritto ` una suce
cessione di n file, ciascuno contenente una funzione BubbleSort che differisce da quella contenuta nel file immediatamente precedente e successivo
solamente per una modifica al corrispondente AST.

4.3.4

Ordinamento con BubbleSort modificato

L’ordinamento dei vettori con le versioni mutate di BubbleSort inizia
trascinando tutti i file ModBubble*.js all’interno della drop area nella
pagina HTML descritta sopra.
25

Prove eseguite

La drop area di figura 4.6 ` stata ottenuta mediante l’inserimento
e
di due tag <div> annidati; il primo funge da contenitore, il secondo `
e
associato agli eventi necessari alla cattura dei file:
1
2
3

< div id = " drop_container " >
< div id = " drop_zone " > Drop Algorithms Here ! </ div >
</ div >

Gli eventi necessari alla cattura dei file sono due: handleDragOver e
handleFileSelect. Il primo serve a rendere l’effetto visivo della disponibilit` ad accogliere file durante il loro trascinamento nella drop area,
a
il secondo si occupa effettivamente di leggerli e di eseguire le versioni di
BubbleSort in essi contenute.
L’associazione degli eventi alla drop area avviene per mezzo delle
istruzioni sottostanti:
1
2

3

var dropZone = document . getElementById ( ’ drop_zone ’) ;
dropZone . addEventListener ( ’ dragover ’ , handleDragOver ,
false ) ;
dropZone . addEventListener ( ’ drop ’ , handleFileSelect ,
false ) ;

Il contenuto della funzione handleDragOver() ` il seguente:
e
1
2
3
4
5

function handleDragOver ( evt ) {
evt . stopPropagation () ;
evt . preventDefault () ;
evt . dataTransfer . dropEffect = ’ copy ’;
}

Il contenuto della funzione handleFileSelect() ` invece questo:
e
1
2
3

function handleFileSelect ( evt ) {
evt . stopPropagation () ;
evt . preventDefault () ;

4
5
6
7
8
9

var files = evt . dataTransfer . files ;
perfs = new Array ( files . length ) ;
bubbles = new Array ( files . length ) ;
fnames = new Array ( files . length ) ;
w = 0;

10
11
12

strPerfIndexes = " <h3 > PERFORMANCE INDEXES </ h3 > " ;
var txtPerfMatrix = document . getElementById ( "
txtPerfMatrix " ) ;
4. Implementazione

26

setHidden ( txtPerfMatrix , false ) ;

13
14

for ( var i =0 , f ; f = files [ i ]; i ++) {
if (! f . type . match ( ’ javascript ’) ) {
continue ;
}
var reader = new FileReader () ;
fnames [ i ] = f . name ;
reader . onload = loaded ;
reader . readAsText ( f ) ;
}

15
16
17
18
19
20
21
22
23
24

}

Brevemente, la variabile files ` di tipo FileList, un vettore di
e
oggetti File i quali sono a loro volta interfacce ai file selezionati. Queste
funzionalit` sono messe a disposizione dalla File API di HTML5 (http:
a
//www.w3.org/TR/FileAPI/).
All’interno del ciclo for viene innanzitutto verificato che il file attuale sia effettivamente un sorgente JavaScript; se lo ` si procede con le
e
istruzioni successive, altrimenti si passa ad analizzare il prossimo file (se
c’`).
e
L’oggetto reader, istanziazione di FileReader, mette a disposizione
la funzionalit` di lettura del contenuto del file.
a
L’istruzione reader.onload = loaded; serve per lanciare la funzione loaded() una volta chiamata reader.readAsText(f);.
La funzione loaded() ` proprio quella che estrae il contenuto del file,
e
lo valuta, ne crea un oggetto function e la applica ai vettori da ordinare:
1
2
3

function loaded ( evt ) {
var fileString = evt . target . result ;
bubbles [ w ] = fileString ;

4
5
6
7
8
9
10

11
12
13
14
15
16

try {
newBubbleSort = eval ( " ( " + fileString + " ) " ) ;
ordermatrix = matrixOrder ( omatrix ) ;
var perfArrays = new Array ( rows ) ;
for ( var j =0; j < rows ; j ++) {
perfArrays [ j ]= perfIndexArray ( bubblematrix [ j ] ,
ordermatrix [ j ]) ;
}
perfs [ w ] = globalPerfIndex ( perfArrays ) ;
} catch ( e ) {
perfs [ w ] = null ;
} finally {
strPerfIndexes += fnames [ w ]+ " : <b > " + perfs [ w ]+ " </b
> < br / > " ;
27

w ++;
txtPerfMatrix . innerHTML = strPerfIndexes ;

17
18

}

19
20

Prove eseguite

}

Viene poi calcolato l’indice di prestazione (par. 4.3.5) relativo a quella
mutazione di BubbleSort e viene scritto sulla pagina HTML (riga 18)
sotto la drop area.

Figura 4.7: Output degli indici di prestazione.

4.3.5

Indici di prestazione

L’indice di prestazione tra due vettori costituiti dagli stessi elementi ` qui
e
definito come media aritmetica delle reciproche distanze tra la posizione
occupata dall’i-esimo elemento nel primo vettore e la posizione occupata
dallo stesso elemento nel secondo vettore:
k=

1
n

d(i, j)
i,j

Supponendo che il primo vettore sia v = [3, 1, 2] e che il secondo sia
u = [1, 2, 3], si procede al calcolo del vettore w, in cui l’i-esimo elemento rappresenta il numero di posizioni per cui differisce v[i] dallo stesso
elemento all’interno di u.
Nel caso in esame, il numero 3 occupa la prima posizione in v e in u
occupa la terza posizione; la distanza vale dunque w[1] = |3 − 1| = 2.
Completando il calcolo delle distanze ai due elementi rimanenti, si
ottiene w = [2, 1, 1], il quale genera un indice di prestazione k pari a
(2 + 1 + 1)/3 = 4/3 1, 33.
4. Implementazione

28

Si noti che nel caso in cui k = 0 allora i due vettori sono ordinati allo
stesso modo (e quindi vi ` massima similitudine), mentre all’aumentare
e
di k aumenta il grado di mutazione nelle posizioni degli elementi.
Detta n la lunghezza dei vettori da confrontare, un limite superiore
per k ` k = n/2 se n ` pari, mentre ` k = 2 (n − l)/n con l = (n−1), (n−
e
e
e
3), · · · , 2 se n ` dispari (situazioni che corrisponderebbero all’inversione
e
totale delle posizioni degli elementi tra il primo ed il secondo vettore);
da queste considerazioni, si pu` affermare che
o
0≤k≤

n
2 (n − l)/n

se n pari
se n dispari

Non vi sono situazioni di ambiguit` nel calcolo dell’indice k poich´,
a
e
per come sono stati generati i vettori di partenza (par. 4.3.1), nessuno
di essi contiene elementi ripetuti.
Ora che ` stato definito l’indice di prestazione relativo ad una singola
e
coppia di vettori, si pu` introdurre la definizione di indice globale di
o
prestazione, denotato con K: esso rappresenta la media aritmetica degli
indici di prestazioni kr relativi a tutti i vettori generati:
1
K=
n

n

kr
r=1

Questo indice ` significativo in quanto, data una mutazione τ di
e
BubbleSort originale, K rappresenta sinteticamente la correttezza del
risultato rispetto a quello desiderato.
Diremo che τ ` una mutazione equivalente se, per ogni vettore d’ine
gresso, il risultato prodotto ` uguale a quello di BubbleSort originale. In
e
tal caso l’indice globale di prestazione Kτ associato a τ vale 0.
Supponiamo, per maggiore chiarezza, che la mutazione τ sia tale
che i vettori da esso generati siano ordinati secondo un ordinamento
decrescente1 (ovvero il contrario di quanto faccia BubbleSort originale).
Partendo dai vettori
x1 = [1, −3, −6]

x2 = [1, 0, 2]

x3 = [−6, 7]

l’esecuzione di BubbleSort originale su di essi produrrebbe i vettori
y1 = [−6, −3, 1]

y2 = [0, 1, 2]

y3 = [−6, 7]

mentre l’esecuzione di τ produrrebbe
z1 = [1, −3, −6]
1

z2 = [2, 1, 0]

z3 = [7, −6]

Si tratta di una situazione prettamente esemplificativa, in quanto una simile mutazione ` impossibile nel presente lavoro (qui si considerano mutazioni costituite da
e
scambi di nodi dell’AST).
29

Prove eseguite

Per valutare la “bont`” di τ (nel senso di cui sopra) si procede dunque
a
al calcolo degli indici kr :
k1 = (2+0+2)/3

1, 33

k2 = (2+0+2)/3

1, 33

k3 = (1+1)/2 = 1

e dunque l’indice di prestazione globale associato a τ vale
Kτ = (k1 + k2 + k3 )/3 = (1, 33 + 1, 33 + 1)/3

1, 22

Pu` capitare, e anzi ` una situazione piuttosto frequente, che per
o
e
qualche τ il risultato della sua esecuzione non sia un vettore: a tal
proposito si veda il paragrafo 5.1.2.
L’algoritmo per il calcolo dei kr ` il seguente:
e
1
2

3

function perfIndexArray ( a1 , a2 ) {
return Math . round ( sumArray ( rankArray ( a1 , a2 ) ) / a1 .
length *100) /100;
}

dove sumArray(v) restituisce la somma degli elementi del vettore v,
mentre la funzione rankArray(a,b) ` definita in questo modo:
e
1
2
3
4
5
6
7
8
9
10
11
12
13
14

function rankArray ( a1 , a2 ) {
var matched ;
var distances = new Array ( a1 . length ) ;
for ( var i =0; i < a1 . length ; i ++) {
matched = false ;
for ( var j =0; j < a2 . length && ! matched ; j ++) {
if ( a1 [ i ]== a2 [ j ]) {
distances [ i ] = Math . abs (i - j ) ;
matched = true ;
}
}
}
return distances ;
}

Per quanto riguarda infine il calcolo dell’indice globale di prestazione
Kτ , l’algoritmo che lo calcola ` il seguente:
e
1
2

3

function globalPerfIndex ( perfArray ) {
return Math . round ( sumArray ( perfArray ) / perfArray .
length *100) /100;
}

dove il vettore perfArray contiene tutti gli indici kr .
Capitolo

5

Risultati
In questo capitolo verranno discussi i risultati ottenuti a fronte dell’esecuzione degli esperimenti.

5.1

Situazioni possibili

Si esamineranno nel seguito le situazioni che si possono verificare dopo
l’esecuzione di una mutazione di BubbleSort originale.

5.1.1

Ordinamento corretto

Il caso di ordinamento corretto si verifica dopo l’esecuzione dell’algoritmo
BubbleSort originale o di una sua mutazione equivalente1 τeq .
Come risultato, si avranno dei vettori correttamente ordinati in senso
crescente, degli indici di prestazione kr tutti nulli e un indice globale di
prestazione associato all’algoritmo in esame Kτ anch’esso nullo.

5.1.2

Ordinamento con mutazione non equivalente

Se la mutazione τ di BubbleSort non ` equivalente, vi sono le seguenti
e
situazioni mutualmente esclusive:
• Errore a runtime: τ produce un errore durante la sua esecuzione
che ne provoca la prematura terminazione
• Output diverso da vettore: τ viene eseguito senza intoppi ma
l’oggetto restituito non ` un vettore
e
• Output ` un vettore: τ viene eseguito senza intoppi e l’oggetto
e
restituito ` un vettore non correttamente ordinato
e
1

Si veda il paragrafo 4.3.5.

31
5. Risultati

32

• Ciclo infinito: τ resta bloccato in un ciclo infinito e non termina
mai
I primi due casi, seppure rappresentino l’esito della maggioranza degli
esperimenti, hanno l’effetto di generare degli indici kr,τ (intendendo con
tale notazione l’indice di prestazione riferito al vettore r dopo l’esecuzione
di τ ) e Kτ non ben definiti; si ` scelto quindi di comportarsi nei seguenti
e
modi:
• Errore a runtime: kr,τ = null e Kτ = null
• Output diverso da vettore: kr,τ = NaN e Kτ = NaN
Il caso invece di corretta esecuzione di τ e di output di un vettore
(inevitabilmente non correttamente ordinato data l’ipotesi di mutazione
non equivalente) presenter` degli indici di prestazione globali e non gloa
bali tutti diversi da zero; l’unica situazione in cui essi sono nulli ` che
e
tutti i vettori da ordinare siano costituiti da un solo elemento (situazione
banale).
Tali indici saranno tanto pi` elevati tanto pi` diverso dalla situazione
u
u
ideale sar` l’ordinamento degli elementi dei vettori prodotti.
a
Infine, nel caso di ciclo infinito, non viene prodotto alcun indice di
prestazione, dato che ` necessario forzare la terminazione del programma.
e

5.2

Analisi dei risultati

Per avere un quadro suffucientemente ampio dei risultati ottenuti, sono
stati condotti due esperimenti; il primo producendo 1000 mutazioni di
BubbleSort, il secondo producendone 5000.
Per ogni esperimento sono stati elaborati due grafici:
• Grafico di tentativi di scambio per mutazione: mostra quanti
tentativi di scambio si sono resi necessari per passare dalla mutazione τi alla mutazione τi+1 ; in ascisse ` riportato il numero di
e
mutazione, mentre in ordinate i corrispondenti numeri di scambi
richiesti
• Grafico di errori a runtime: mostra quante mutazioni hanno prodotto un errore a runtime e quante invece hanno terminato
correttamente
Un terzo grafico, detto grafico degli indici globali di prestazione,
che mostra l’andamento degli indici Kτ al variare della mutazione τ , non `
e
stato incluso nel presente lavoro poich´ non fornisce alcuna informazione
e
significativa a causa dei risultati ottenuti.
33

Analisi dei risultati

Per entrambi gli esperimenti sono stati presi in esame 30 vettori,
ciascuno dei quali costituito da un numero di elementi variabile da 10 a
30 e ogni elemento pu` assumere valori interi nel range [−100, 100].
o

5.2.1

1000 mutazioni

Si analizza per prima cosa il numero di tentativi di scambio per mutazione: dai dati raccolti si osserva che si va da un minimo di 1 tentativo di scambio ad un massimo di 34 tentativi, con una media di 5,14
tentativi/mutazione.

50

40

30

20

10

0
0

200

400

600

800

1000

Per quanto riguarda l’esito dell’esecuzione degli algoritmi mutati, `
e
emerso che 968/1000 hanno prodotto errori a runtime terminando prematuramente, 31/1000 sono stati interamente eseguiti e 1/1000 presentava
un ciclo infinito che ha richiesto la terminazione forzata del programma.
34

5. Risultati

1000
900
800
700
600
500
400
300
200
100
0

Non eseguiti

Eseguiti

Cicli infiniti

Delle 31 mutazioni che hanno terminato correttamente la propria
esecuzione, solo 2 hanno restituito vettori; si tratta in particolare delle
prime 2 mutazioni:
Mutazione
τ0
τ1
τ2
···
τ999

Indice Kτ
6,29
6,30
null
null o NaN
null

Tabella 5.1: Indici globali di prestazione nel primo esperimento.

Un indice Kτ pari a circa 6 significa che mediamente ogni vettore
ordinato da quella mutazione presenta elementi distanti 6 posizioni da
quelle che dovrebbero assumere nel caso di ordinamento corretto.

5.2.2

5000 mutazioni

In questo secondo esperimento il numero di tentativi di scambio per mutazione sono i seguenti: si va da un minimo di 1 tentativo di scambio ad
un massimo di 45 tentativi, con una media di 25,92 tentativi/mutazione.
35

Analisi dei risultati

50

40

30

20

10

0
0

1000

2000

3000

4000

5000

L’esito dell’esecuzione degli algoritmi mutati ha evidenziato che 4923/5000
hanno terminato prematuramente a causa di errori a runtime, 49/5000
sono stati eseguiti per intero e i rimanenti 28/5000 non terminavano a
causa di cicli infiniti.

5000
4500
4000
3500
3000
2500
2000
1500
1000
500
0

Non eseguiti

Eseguiti

Cicli infiniti

Delle 49 mutazioni che hanno correttamente terminato la propria
esecuzione, nessuna di esse ha restituito vettori e dunque gli indici globali
di prestazione assumono tutti o valori null oppure NaN:
36

5. Risultati

Mutazione
τ0
τ1
τ2
···
τ999

Indice Kτ
null
null
null
null o NaN
null

Tabella 5.2: Indici globali di prestazione nel secondo esperimento.

Ci` significa che ogni mutazione priva di cicli infiniti non produce
o
vettori, per ogni input.

5.2.3

Confronto tra i risultati

Nel primo esperimento, quello che coinvolge 1000 mutazioni di BubbleSort, si sono evidenziati risultati pi` soddisfacenti rispetto al secondo
u
esperimento, sia in termini di numero medio di tentativi di scambio necessari per passare dalla mutazione τi alla mutazione τi+1 (5,14 contro
25,92), sia in termini di numero di mutazioni che hanno prodotto vettori
(2 contro 0).
La seguente tabella riassume la situazione:

Mutazioni coinvolte
Media scambi/mutazione
Mutazioni con errori a runtime
Mutazioni eseguite interamente
Mutazioni con cicli infiniti
Mutazioni con output vettore

Esperimento 1
1000
5,14
96,80%
3,10%
0,10%
0,20%

Esperimento 2
5000
25,92
98,46%
0,98%
0,56%
0,00%

Tabella 5.3: Comparazione dei risultati.

In ultima analisi, all’aumentare del numero di mutazioni generate diminuisce la percentuale di mutazioni che producono vettori (solitamente
solo le prime lo fanno).
Non ci si deve stupire del fatto di aver ottenuto una cos` bassa perı
centuale di mutazioni pseudocorrette: le modifiche successive effettuate
agli AST sono di natura casuale e consistono nello scambio di nodi; ci` si
o
traduce in un codice sorgente che inevitabilmente subisce grandi modifiche anche per (apparentemente) piccole mutazioni (si pensi ad esempio
allo scambio tra la primissima istruzione e lo statement return).
Capitolo

6

Conclusioni
L’obiettivo di questo lavoro di tesi ` stato quello di studiare gli effetti
e
prodotti da una serie di mutazioni successive nel codice di una funzione
JavaScript sui dati d’ingresso.
Quanto esposto nei capitoli precedenti permette di affermare che tale
obiettivo sia stato pienamente raggiunto.
Il software sviluppato permette, infatti, di eseguire un numero a piacere di mutazioni su una funzione JavaScript memorizzata su file che
viene chiesta in ingresso; a partire da questa, il software esegue tutte
le elaborazioni necessarie e, oltre ai file contenenti le versioni modificate
della funzione originale, produce inoltre una statistica circa il numero di
tentativi di scambio eseguiti per ogni mutazione effettiva.
Un secondo software sviluppato in JavaScript si occupa poi di generare i vettori su cui eseguire i test, di prendere in ingresso l’insieme
dei file contenenti gli algoritmi mutati e di generare gli indici globali di
prestazione.

6.1

Sviluppi futuri

Una possibile estensione di quanto qui svolto potrebbe essere quella di
considerare un numero maggiore di mutazioni e lavorare su pi` funzioni
u
iniziali, al fine di giungere ad un probabile miglioramento degli indici
globali di prestazione.
Un’altra strada percorribile che richiede, almeno in prima battuta, la
sola scrittura di nuove grammatiche, ` quella di prendere in considerae
zione altri linguaggi di programmazione.
In ogni caso, in questa tesi la grammatica ` stata sviluppata in modo
e
tale da isolare ogni singola parte degli statement previsti; ` ragionee
37
6. Conclusioni

38

vole pensare che considerando invece statement interi la probabilit` di
a
giungere a mutazioni fallimentari diminuisca.
Capitolo

7

Ringraziamenti
Desidero ringraziare in maniera sentita il prof. Alberto Bartoli
per avermi offerto la possibilit` di far parte del suo laboratorio,
a
concedendomi, cos` di trascorrere un periodo
ı,
a stretto contatto con il mondo della ricerca.
Un grazie al prof. Medvet, o meglio Eric,
che si ` dimostrato spesso polemico ma sempre costruttivo.
e
Un grazie al dott. De Lorenzo,
per noialtri Andrea, sempre disponibile
a chiarire tutti i miei dubbi e senza il quale
questo lavoro non sarebbe stato n´ meno difficile
e
n´ pi` divertente :P
e u
Impossibile dimenticare
Giulio e Daniele
gli altri tesisti che hanno lavorato
con me nel mai banale Machine Learning Lab.
Un ringraziamento
che non pu` trovare lo spazio appropriato
o
fra le ultime righe di questa tesi,
lo devo ai miei amici,
vecchi e nuovi,
ed ai miei fantastici coinquilini,
che hanno allietato il mio passato,
che rendono unico il mio presente,
e che spero continueranno a farmi dono
della loro presenza nel mio futuro.
Non mi sembra opportuno ringraziarli ad uno ad uno,
perch´ sarebbe un elenco troppo lungo
e
e soprattutto perch´ citare i loro nomi
e
39
Ringraziamenti

40

conferirebbe un ordine implicito
che non renderebbe giustizia
e non rispecchierebbe
i miei sentimenti.
Un immenso grazie va ad Alessio,
per avermi sostenuto in questo percorso
universitario, per essersi sub` i miei
ıto
sfoghi pi` o meno fondati ma, soprattutto,
u
per avermi sopportato tutti questi anni e,
spero, continuer` a farlo per molti anni a venire.
a
Infine, il mio pensiero va
ai miei genitori
i quali mi hanno dato tutto
un affetto senza limiti,
una giusta educazione,
un sostegno nei periodi di necessit`.
a
. . . ai miei genitori
che hanno favorito le mie passioni
e che tuttora si prodigano in questa direzione.
. . . ai miei genitori
perch´ credo
e
non avrei potuto averne di migliori.

Come ultimissima cosa vorrei invece
invitare chi mi ha ripetutamente smontato
e criticato a farsi un esame di coscienza ed
ammettere che, in fondo, qualche torto ce l’avesse.
Auguro a costoro di guarire dal brutto
morbo dell’invidia.

More Related Content

What's hot

Clean programming 2020-01-25 @ Modena Tech Summit
Clean programming 2020-01-25 @ Modena Tech SummitClean programming 2020-01-25 @ Modena Tech Summit
Clean programming 2020-01-25 @ Modena Tech SummitDavide Muzzarelli
 
Python@Unina - Theory
Python@Unina - TheoryPython@Unina - Theory
Python@Unina - TheoryNaLUG
 
Javaday 2006: Java 5
Javaday 2006: Java 5Javaday 2006: Java 5
Javaday 2006: Java 5Matteo Baccan
 
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...Paolo Oltramonti
 
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...Paolo Oltramonti
 
High Level Synthesis Using Esterel
High Level Synthesis Using EsterelHigh Level Synthesis Using Esterel
High Level Synthesis Using EsterelAlberto Minetti
 
Laboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabiliLaboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabiliMajong DevJfu
 
Design Pattern Comportamentali
Design Pattern ComportamentaliDesign Pattern Comportamentali
Design Pattern ComportamentaliRiccardo Cardin
 
Caratteristiche del linguaggio c
Caratteristiche del linguaggio cCaratteristiche del linguaggio c
Caratteristiche del linguaggio cughetta
 

What's hot (12)

Clean programming 2020-01-25 @ Modena Tech Summit
Clean programming 2020-01-25 @ Modena Tech SummitClean programming 2020-01-25 @ Modena Tech Summit
Clean programming 2020-01-25 @ Modena Tech Summit
 
Python@Unina - Theory
Python@Unina - TheoryPython@Unina - Theory
Python@Unina - Theory
 
Javaday 2006: Java 5
Javaday 2006: Java 5Javaday 2006: Java 5
Javaday 2006: Java 5
 
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
 
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
Conversione diretta ed inversa tra automi a stati non definiti (nfa) ed espre...
 
High Level Synthesis Using Esterel
High Level Synthesis Using EsterelHigh Level Synthesis Using Esterel
High Level Synthesis Using Esterel
 
Laboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabiliLaboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabili
 
Design Pattern Comportamentali
Design Pattern ComportamentaliDesign Pattern Comportamentali
Design Pattern Comportamentali
 
Gcc & Make
Gcc & MakeGcc & Make
Gcc & Make
 
Caratteristiche del linguaggio c
Caratteristiche del linguaggio cCaratteristiche del linguaggio c
Caratteristiche del linguaggio c
 
Lezione 2 Corso Python
Lezione 2 Corso PythonLezione 2 Corso Python
Lezione 2 Corso Python
 
Corso Python 2020 - Lezione 2
Corso Python 2020 - Lezione 2Corso Python 2020 - Lezione 2
Corso Python 2020 - Lezione 2
 

Similar to Progetto e realizzazione di uno strumento per la modifica sistematica di codice JavaScript finalizzato al testing

Analisi delle differenze strutturali nelle espressioni regolari costruite da ...
Analisi delle differenze strutturali nelle espressioni regolari costruite da ...Analisi delle differenze strutturali nelle espressioni regolari costruite da ...
Analisi delle differenze strutturali nelle espressioni regolari costruite da ...Marco Potok
 
Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...
Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...
Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...Marco Virgo
 
Traduzione documento Backus ICIP 1959.pdf
Traduzione documento Backus ICIP 1959.pdfTraduzione documento Backus ICIP 1959.pdf
Traduzione documento Backus ICIP 1959.pdfCADZINE
 
Compressione di insiemi di espressioni regolari tramite programmazione geneti...
Compressione di insiemi di espressioni regolari tramite programmazione geneti...Compressione di insiemi di espressioni regolari tramite programmazione geneti...
Compressione di insiemi di espressioni regolari tramite programmazione geneti...Simone Cumar
 
Capitolo 7 elementi di programmazione c-c++
Capitolo 7   elementi di programmazione  c-c++Capitolo 7   elementi di programmazione  c-c++
Capitolo 7 elementi di programmazione c-c++Giovanni Della Lunga
 
Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...
Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...
Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...DamianoRavalico
 
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...Idriss Riouak
 
2011.02.19 Introducing F#
2011.02.19 Introducing F#2011.02.19 Introducing F#
2011.02.19 Introducing F#Marco Parenzan
 
Analisi e realizzazione di uno strumento per la verifica di conformità su sis...
Analisi e realizzazione di uno strumento per la verifica di conformità su sis...Analisi e realizzazione di uno strumento per la verifica di conformità su sis...
Analisi e realizzazione di uno strumento per la verifica di conformità su sis...Davide Bravin
 
Capitolo 2 elementi di programmazione in vba
Capitolo 2   elementi di programmazione in vbaCapitolo 2   elementi di programmazione in vba
Capitolo 2 elementi di programmazione in vbaGiovanni Della Lunga
 
Estendere Java con il Meta Programming System di JetBrains
Estendere Java con il Meta Programming System di JetBrains Estendere Java con il Meta Programming System di JetBrains
Estendere Java con il Meta Programming System di JetBrains Federico Tomassetti
 
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...LUSPIO LanguageCamp
 
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...LUSPIO LanguageCamp
 
Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...
Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...
Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...Stefano Costanzo
 
Assembly and Reverse Engineering
Assembly and Reverse EngineeringAssembly and Reverse Engineering
Assembly and Reverse Engineeringluigi capuzzello
 
Corso LaTeX - Lezione Uno: Cos'é LaTeX?
Corso LaTeX - Lezione Uno: Cos'é LaTeX?Corso LaTeX - Lezione Uno: Cos'é LaTeX?
Corso LaTeX - Lezione Uno: Cos'é LaTeX?Valentina Ferro
 
Linguaggi di programmazione
Linguaggi di programmazioneLinguaggi di programmazione
Linguaggi di programmazionedibari.92
 

Similar to Progetto e realizzazione di uno strumento per la modifica sistematica di codice JavaScript finalizzato al testing (20)

Analisi delle differenze strutturali nelle espressioni regolari costruite da ...
Analisi delle differenze strutturali nelle espressioni regolari costruite da ...Analisi delle differenze strutturali nelle espressioni regolari costruite da ...
Analisi delle differenze strutturali nelle espressioni regolari costruite da ...
 
Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...
Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...
Classificazione di frasi in linguaggio naturale per il riconoscimento di inte...
 
Traduzione documento Backus ICIP 1959.pdf
Traduzione documento Backus ICIP 1959.pdfTraduzione documento Backus ICIP 1959.pdf
Traduzione documento Backus ICIP 1959.pdf
 
Compressione di insiemi di espressioni regolari tramite programmazione geneti...
Compressione di insiemi di espressioni regolari tramite programmazione geneti...Compressione di insiemi di espressioni regolari tramite programmazione geneti...
Compressione di insiemi di espressioni regolari tramite programmazione geneti...
 
Capitolo 7 elementi di programmazione c-c++
Capitolo 7   elementi di programmazione  c-c++Capitolo 7   elementi di programmazione  c-c++
Capitolo 7 elementi di programmazione c-c++
 
Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...
Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...
Progettazione e Sviluppo di un Sistema per Migliorare il Codice Generato da u...
 
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
 
2011.02.19 Introducing F#
2011.02.19 Introducing F#2011.02.19 Introducing F#
2011.02.19 Introducing F#
 
Analisi e realizzazione di uno strumento per la verifica di conformità su sis...
Analisi e realizzazione di uno strumento per la verifica di conformità su sis...Analisi e realizzazione di uno strumento per la verifica di conformità su sis...
Analisi e realizzazione di uno strumento per la verifica di conformità su sis...
 
Capitolo 2 elementi di programmazione in vba
Capitolo 2   elementi di programmazione in vbaCapitolo 2   elementi di programmazione in vba
Capitolo 2 elementi di programmazione in vba
 
Estendere Java con il Meta Programming System di JetBrains
Estendere Java con il Meta Programming System di JetBrains Estendere Java con il Meta Programming System di JetBrains
Estendere Java con il Meta Programming System di JetBrains
 
Prova Tesi
Prova TesiProva Tesi
Prova Tesi
 
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
 
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
Sistemi autore, linguaggio controllato e manualistica aziendale: scrivere per...
 
Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...
Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...
Definizione e sviluppo di un algoritmo genetico multiobiettivo per problemi d...
 
Object Oriented Programming
Object Oriented ProgrammingObject Oriented Programming
Object Oriented Programming
 
Assembly and Reverse Engineering
Assembly and Reverse EngineeringAssembly and Reverse Engineering
Assembly and Reverse Engineering
 
Bash programming
Bash programmingBash programming
Bash programming
 
Corso LaTeX - Lezione Uno: Cos'é LaTeX?
Corso LaTeX - Lezione Uno: Cos'é LaTeX?Corso LaTeX - Lezione Uno: Cos'é LaTeX?
Corso LaTeX - Lezione Uno: Cos'é LaTeX?
 
Linguaggi di programmazione
Linguaggi di programmazioneLinguaggi di programmazione
Linguaggi di programmazione
 

Recently uploaded

Ticonzero news 148.pdf aprile 2024 Terza cultura
Ticonzero news 148.pdf aprile 2024 Terza culturaTiconzero news 148.pdf aprile 2024 Terza cultura
Ticonzero news 148.pdf aprile 2024 Terza culturaPierLuigi Albini
 
Corso di digitalizzazione e reti per segretario amministrativo
Corso di digitalizzazione e reti per segretario amministrativoCorso di digitalizzazione e reti per segretario amministrativo
Corso di digitalizzazione e reti per segretario amministrativovaleriodinoia35
 
lezione di fisica_I moti nel piano_Amaldi
lezione di fisica_I moti nel piano_Amaldilezione di fisica_I moti nel piano_Amaldi
lezione di fisica_I moti nel piano_Amaldivaleriodinoia35
 
Esperimenti_laboratorio di fisica per la scuola superiore
Esperimenti_laboratorio di fisica per la scuola superioreEsperimenti_laboratorio di fisica per la scuola superiore
Esperimenti_laboratorio di fisica per la scuola superiorevaleriodinoia35
 
IL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla Cresima
IL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla CresimaIL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla Cresima
IL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla CresimaRafael Figueredo
 
XIII Lezione - Arabo G.Rammo @ Libera Accademia Romana
XIII Lezione - Arabo G.Rammo @ Libera Accademia RomanaXIII Lezione - Arabo G.Rammo @ Libera Accademia Romana
XIII Lezione - Arabo G.Rammo @ Libera Accademia RomanaStefano Lariccia
 
CON OCCHI DIVERSI - catechesi per candidati alla Cresima
CON OCCHI DIVERSI - catechesi per candidati alla CresimaCON OCCHI DIVERSI - catechesi per candidati alla Cresima
CON OCCHI DIVERSI - catechesi per candidati alla CresimaRafael Figueredo
 
RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.
RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.
RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.giuliofiorerm
 
La seconda guerra mondiale per licei e scuole medie
La seconda guerra mondiale per licei e scuole medieLa seconda guerra mondiale per licei e scuole medie
La seconda guerra mondiale per licei e scuole medieVincenzoPantalena1
 
XI Lezione - Arabo LAR Giath Rammo @ Libera Accademia Romana
XI Lezione - Arabo LAR Giath Rammo @ Libera Accademia RomanaXI Lezione - Arabo LAR Giath Rammo @ Libera Accademia Romana
XI Lezione - Arabo LAR Giath Rammo @ Libera Accademia RomanaStefano Lariccia
 

Recently uploaded (10)

Ticonzero news 148.pdf aprile 2024 Terza cultura
Ticonzero news 148.pdf aprile 2024 Terza culturaTiconzero news 148.pdf aprile 2024 Terza cultura
Ticonzero news 148.pdf aprile 2024 Terza cultura
 
Corso di digitalizzazione e reti per segretario amministrativo
Corso di digitalizzazione e reti per segretario amministrativoCorso di digitalizzazione e reti per segretario amministrativo
Corso di digitalizzazione e reti per segretario amministrativo
 
lezione di fisica_I moti nel piano_Amaldi
lezione di fisica_I moti nel piano_Amaldilezione di fisica_I moti nel piano_Amaldi
lezione di fisica_I moti nel piano_Amaldi
 
Esperimenti_laboratorio di fisica per la scuola superiore
Esperimenti_laboratorio di fisica per la scuola superioreEsperimenti_laboratorio di fisica per la scuola superiore
Esperimenti_laboratorio di fisica per la scuola superiore
 
IL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla Cresima
IL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla CresimaIL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla Cresima
IL CHIAMATO ALLA CONVERSIONE - catechesi per candidati alla Cresima
 
XIII Lezione - Arabo G.Rammo @ Libera Accademia Romana
XIII Lezione - Arabo G.Rammo @ Libera Accademia RomanaXIII Lezione - Arabo G.Rammo @ Libera Accademia Romana
XIII Lezione - Arabo G.Rammo @ Libera Accademia Romana
 
CON OCCHI DIVERSI - catechesi per candidati alla Cresima
CON OCCHI DIVERSI - catechesi per candidati alla CresimaCON OCCHI DIVERSI - catechesi per candidati alla Cresima
CON OCCHI DIVERSI - catechesi per candidati alla Cresima
 
RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.
RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.
RICERCA_SUGLI ANFIBI PER LA PRIMA MEDIA.
 
La seconda guerra mondiale per licei e scuole medie
La seconda guerra mondiale per licei e scuole medieLa seconda guerra mondiale per licei e scuole medie
La seconda guerra mondiale per licei e scuole medie
 
XI Lezione - Arabo LAR Giath Rammo @ Libera Accademia Romana
XI Lezione - Arabo LAR Giath Rammo @ Libera Accademia RomanaXI Lezione - Arabo LAR Giath Rammo @ Libera Accademia Romana
XI Lezione - Arabo LAR Giath Rammo @ Libera Accademia Romana
 

Progetto e realizzazione di uno strumento per la modifica sistematica di codice JavaScript finalizzato al testing

  • 1. ` UNIVERSITA DEGLI STUDI DI TRIESTE ` FACOLTA DI INGEGNERIA Corso di Laurea Triennale in Ingegneria dell’Informazione Tesi di Laurea in Reti di Calcolatori Progetto e realizzazione di uno strumento per la modifica sistematica di codice JavaScript finalizzato al testing LAUREANDO RELATORE Dennis Morello prof. Alberto Bartoli CORRELATORI prof. Eric Medvet dott. Andrea De Lorenzo Anno Accademico 2012/2013
  • 2.
  • 4.
  • 5. Indice 1 Introduzione 1 2 Scenario 2.1 Scopo del progetto . . . . . . . . 2.2 Language parsing . . . . . . . . . 2.2.1 Grammatiche . . . . . . . 2.3 ANTLR . . . . . . . . . . . . . . 2.3.1 Cos’` ANTLR? . . . . . . e 2.3.2 Grammatiche ANTLR . . 2.3.2.1 Lexer Grammar 2.3.2.2 Parser Grammar 2.3.2.3 Tree Grammar . 2.4 Abstract Syntax Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 5 5 6 7 8 8 9 3 Progettazione 3.1 Specifiche del problema . . . . . . 3.2 Manipolazione di codice sorgente . 3.2.1 Da codice sorgente ad AST 3.2.2 Manipolazione di AST . . . 3.2.3 Da AST a codice sorgente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 11 11 13 15 4 Implementazione 4.1 Impiego di ANTLR . . . . . . . . . . . . . . . 4.1.1 ANTLRWorks . . . . . . . . . . . . . 4.2 Ordinamento di un vettore . . . . . . . . . . 4.2.1 BubbleSort . . . . . . . . . . . . . . . 4.2.1.1 Pseudo-codice di BubbleSort 4.3 Prove eseguite . . . . . . . . . . . . . . . . . . 4.3.1 Generazione casuale di vettori . . . . . 4.3.2 Ordinamento con BubbleSort originale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 17 18 19 19 20 21 23 v
  • 6. vi INDICE 4.3.3 4.3.4 4.3.5 Modifica di BubbleSort . . . . . . . . . . . . . . . Ordinamento con BubbleSort modificato . . . . . . Indici di prestazione . . . . . . . . . . . . . . . . . 5 Risultati 5.1 Situazioni possibili . . . . . . . . . 5.1.1 Ordinamento corretto . . . 5.1.2 Ordinamento con mutazione 5.2 Analisi dei risultati . . . . . . . . . 5.2.1 1000 mutazioni . . . . . . . 5.2.2 5000 mutazioni . . . . . . . 5.2.3 Confronto tra i risultati . . . . . . . . . . . . . . . . . . . . . . non equivalente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 24 27 31 31 31 31 32 33 34 36 6 Conclusioni 37 6.1 Sviluppi futuri . . . . . . . . . . . . . . . . . . . . . . . . 37 7 Ringraziamenti 39
  • 7. Capitolo 1 Introduzione Modificare in modo automatico e sistematico il codice sorgente di un programma pu` essere utile in varie applicazioni, quali ad esempio la o correzione automatica di bug. Un modo per raggiungere questo scopo ` quello che prevede un ape proccio evoluzionistico: si generano tante mutazioni casuali al codice sorgente originale e, per ognuna di esse, si valutano i risultati e si sceglie la soluzione migliore. A tal fine, ` necessario mettere a punto uno strumento che autonomae mente esegua modifiche al codice, che si occupi di eseguirlo e che analizzi i risultati conseguiti, confrontandoli con quelli attesi. In questa tesi ` stato progettato e sviluppato uno strumento utile e in questo ambito, in particolare, uno strumento in grado di misurare l’impatto che una modifica al codice sorgente di un programma produce sul suo output, nel senso che verr` illustrato dettagliatamente nei capitoli a successivi. 1
  • 8.
  • 9. Capitolo 2 Scenario Verranno affrontati in questo capitolo alcuni argomenti preliminari e verr` chiarito lo scopo del lavoro di tesi. a 2.1 Scopo del progetto Lo scopo di questo lavoro ` quello di determinare le conseguenze proe dotte da mutazioni successive nella struttura del codice sorgente di una funzione JavaScript. 2.2 Language parsing Prima di affrontare l’argomento che d` il nome a questo paragrafo, ` a e necessario introdurre alcuni concetti. Un linguaggio naturale si definisce a partire da un alfabeto; con i suoi simboli ` possibile formare un insieme di sequenze, dette parole, ma non e tutte le sequenze sono parole del linguaggio naturale. La grammatica del linguaggio fornisce le regole per decidere quali sequenze sono parole del linguaggio. Le parole del linguaggio naturale possono essere raggruppate in insiemi detti frasi, ma anche qui non tutte le frasi appartengono al linguaggio. La sintassi del linguaggio fornisce le regole per decidere quali sequenze sono frasi del linguaggio (si parla di frasi sintatticamente corrette). Un linguaggio di programmazione ` un linguaggio artificiale; per poe terlo definire ` necessario dare ancora qualche definizione. e Dato un insieme finito non vuoto V (chiamato solitamente alfabeto), si definisce universo linguistico su V , e si indica con V ∗, l’insieme delle sequenze finite di lunghezza arbitraria di elementi di V . Un linguaggio L sull’alfabeto V ` allora un sottoinsieme di V ∗. e 3
  • 10. 4 2. Scenario Nel considerare i linguaggi di programmazione, non siamo interessati a tutti i possibili sottoinsiemi di V ∗, ma solo a quelli definibili in maniera finita; questa descrizione pu` essere, per esempio, fornita attraverso una o grammatica (si veda il paragrafo successivo). Una volta definita la grammatica, per language parsing si intende il processo di analisi di stringhe di simboli, in accordo con quanto sancito dalla grammatica. Qui non si intende risalire al significato di una sequenza di caratteri, ma semplicemente riconoscere ed isolare alcune sequenze (che in ambito operativo vengono chiamate token). 2.2.1 Grammatiche In informatica, una grammatica G ` definita da e • Un alfabeto V di simboli terminali • Un alfabeto N di simboli non terminali • Un assioma S ∈ N tale che V ∩ N = ∅ • Un insieme finito di regole sintattiche P del tipo X → α, dove X ∈ N e α ∈ (N ∪ V )∗ e si legge “X produce α” Se in una grammatica vi sono pi` regole aventi la stessa parte sinistra, u ad esempio X → α1 X → α2 ··· X → αn allora esse sono raggruppate usando la convenzione notazionale X → α1 | α1 | · · · | αn La descrizione della grammatica di un linguaggio di programmazione avviene per mezzo di un metalinguaggio formale1 che prende il nome di BNF (Backus-Naur Form, dai nomi dei due studiosi che per primi l’hanno introdotta negli anni ’50) e sue varianti. Il formalismo BNF viene spesso usato non nella sua forma originale, ma utilizzando alcune estensioni che permettono una scrittura pi` concisa u delle grammatiche; si parla allora di EBN F (Extended BNF - non ci si addentrer` nei dettagli, per maggiori informazioni si rimanda all’indirizzo a http://www.cs.cmu.edu/∼pattis/misc/ebnf.pdf). 1 Un metalinguaggio ` un linguaggio usato per parlare di un altro linguaggio. e
  • 11. 5 2.3 2.3.1 ANTLR ANTLR Cos’` ANTLR? e ANTLR2 (ANother Tool for Language Recognition) ` uno strumento e software tipicamente impiegato nella definizione di linguaggi formali e nella costruzione di strumenti che operino su di essi, come traduttori, compilatori o analizzatori di codice sorgente; permette, tra le altre cose, di generare parser di testo e Abstract Syntax Trees (AST) per mezzo della definizione di grammatiche. Una grammatica ANTLR ` un file di testo contenente una successione e di regole che stabiliscano come interpretare il flusso di byte da analizzare (per maggiori dettagli vedasi il paragrafo 2.3.2). Per ogni file di grammatica, ANTLR genera alcuni file sorgenti Java (ma ` possibile, mediante l’inserimento dell’opzione language=x all’intere no della grammatica, generare file sorgenti nello specifico linguaggio x; la lista dei linguaggi supportati da ANTLR 3 ` disponibile all’indirizzo http: e //www.antlr.org/wiki/display/ANTLR3/Code+Generation+Targets/) che verranno poi utilizzati dall’applicazione sviluppata. I file sorgenti generati da ANTLR fanno riferimento a strumenti che riconoscano, analizzino e trasformino dati di input la cui struttura sia stata meticolosamente descritta nella grammatica; tali strumenti sono sostanzialmente i seguenti: Lexer Viene generato a partire dal file contenente la lexer grammar; legge una sequenza di byte (siano essi caratteri o dati binari), li divide in token secondo i pattern specificati (si veda il paragrafo 2.3.2.1) e genera una sequenza di token come output; alcuni di essi (come spazi vuoti o caratteri di punteggiatura) possono essere ignorati ed omessi dall’output. Parser Viene generato a partire dal file contenente la parser grammar; legge una sequenza di token generata dal lexer e li raggruppa in frasi del linguaggio considerato secondo le regole specificate (par. 2.3.2.2); pu` inoltre eseguire alcune azioni aggiuntive, come raffinao re le frasi prodotte, emettere del testo a seconda dei token incontrati (tipico comportamento di un traduttore) o generare un AST che venga poi manipolato per modificare la struttura del linguaggio (` e questo l’uso di ANTLR che ` stato fatto nel presente lavoro di tesi). e Tree Parser Viene generato a partire dal file contenente la tree grammar; visita 2 http://www.antlr.org/
  • 12. 2. Scenario 6 l’albero generato dal parser e, per ogni nodo incontrato, esegue le operazioni eventualmente descritte nella relativa grammatica (par. 2.3.2.3). Tali operazioni possono essere principalmente output di testo o manipolazione di altri nodi. 2.3.2 Grammatiche ANTLR Come accennato nei paragrafi 2.2.1 e 2.3.1, una grammatica ANTLR ` una sequenza di regole che descrivono ciascuna un sottoinsieme della e sintassi del linguaggio; assieme ad esse, una grammatica contiene il suo tipo (lexer grammar o tree grammar ), il suo nome, alcune opzioni, ed eventualmente altre sezioni come la specifica di token, attributi e azioni: grammarType grammar name; <<options>> <<tokens>> <<attributes>> <<actions>> rule1 : ... | ... | ... ; ... ruleN : ... | ... | ... ; La sintassi con cui vengono specificate le regole in ANTLR riprende la notazione Extended Backus-Naur Form (EBNF, si veda paragrafo 2.2.1). In analogia a quanto avviene in Java nella corrispondenza tra il nome di una classe e quello del file che la contiene, in ANTLR il nome della grammatica deve essere uguale al nome del file in cui ` memorizzata; i e file di grammatica hanno usualmente estensione g. La regola da cui inizia il parsing ` chiamata regola di partenza (ogni e regola pu` esserlo); ogni regola consiste a sua volta in una o pi` alternao u tive, che altro non sono che sotto-regole; l’ordine con cui si susseguono ` e ininfluente. A titolo di esempio, una regola chiamata variableDefinition potrebbe avere due alternative, la prima per una semplice definizione di variabile (ad esempio var x;) e la seconda per una definizione con inizializzazione (var x=12;): variableDefinition : declaration | declarationWithInitialization ; Da una generica grammatica T contenuta nel file T.g (supponendo che il linguaggio di programmazione scelto per lo sviluppo del progetto
  • 13. 7 ANTLR sia Java), ANTLR genera i file sorgenti TLexer.java e TParser.java i quali contengono, rispettivamente, il lexer ed il parser per il proprio linguaggio; se sono stati specificati dei token immaginari (ridenominazione di sequenze di caratteri utilizzabili all’interno della grammatica, come ad esempio EQUALS=’=’;), essi vengono memorizzati nel file T.tokens. Nei paragrafi seguenti si analizzeranno le tre tipologie di grammatiche definibili in ANTLR. 2.3.2.1 Lexer Grammar Scopo della lexer grammar ` quello di definire come debbano essere coe struiti i token a partire dall’analisi in prima battuta dell’input assegnato. Un token `, nell’accezione tipica, un insieme di caratteri che rispetti e una particolare regola definita nella lexer grammar. Ad esempio, volendo riconoscere tutti i numeri interi senza segno di lunghezza arbitraria che abbiano come prima cifra un carattere diverso dallo zero, potremmo definire la regola seguente: INT : (’1’..’9’)(’0’..’9’)* ; dove il carattere * indica che il gruppo (’0’..’9’) pu` comparire zero o o pi` volte. u Una volta definite le regole3 per il riconoscimento dei token desiderati, ogni successione di caratteri che abbia senso per la lexer grammar dar` a origine ad un token; se tra le regole definite vi fosse la regola INT descritta nell’esempio sopra, la sequenza di caratteri 0x032C produrrebbe il token 32. Fatte queste premesse, ` possibile affermare che la lexer grammar ` e e una sequenza di modi di riconoscere i token d’interesse: grammar T; ... RULE_1 : ... | ... | ... ; ... RULE_N : ... | ... | ... ; La costruzione dei token ` indispensabile per il passaggio successivo, e quello che vede come protagonista il parser. Terminata la stesura di questa grammatica, ANTLR genera il file TLexer.java, che potr` poi essere utilizzato nel progetto che si sta a sviluppando (T ` il nome della grammatica dell’esempio). e 3 Per convenzione, i nomi delle regole della lexer grammar sono tutte maiuscole.
  • 14. 8 2. Scenario 2.3.2.2 Parser Grammar Una parser grammar ` un insieme di regole necessarie al parser per rie conoscere particolari sequenze di token generate dal lexer; ad esempio, ` possibile riconoscere lo statement return di JavaScript specificando la e regola returnStatement : ’return’ expr ’;’ ; dove expr ` un’opportuna regola. e Valgono le stesse considerazioni fatte per la lexer grammar relativamente alle alternative di una regola. Specificando l’opzione output=AST nella sezione options della grammatica, ` possibile generare l’AST associato al particolare input; per e generare un nodo dell’AST per la regola returnStatement ` sufficiente e modificarne la definizione come segue: returnStatement : ’return’ expr ’;’ -> ^(’return’ expr) ; Tale regola genera un nodo che ha per padre la stringa "return" e per figlio il sotto-albero expr. Per i dettagli su come vengano definiti gli AST in ANTLR si veda il paragrafo ad essi dedicato. Da notare che lexer grammar e parser grammar sono di norma contenute in un unico file. Al termine della stesura della parser grammar, ` possibile utilizzare e ANTLR per generare il file TParser.java. 2.3.2.3 Tree Grammar La tree grammar ` un file contenente una sequenza di regole necessarie e a riconoscere una determinata struttura di un AST. In particolare, ciascuna regola specifica la conformazione di un sottoalbero costituito da un padre e uno o pi` figli (i quali possono essere a u loro volta altri padri o foglie). Ad esempio, la regola seguente riconosce il sotto-albero che rappresenta lo statement return: returnNode : ^(’return’ expr) ; ` E poi possibile eseguire operazioni specifiche in corrispondenza di un nodo che rispetti una determinata regola (ad esempio, emettere del testo
  • 15. 9 Abstract Syntax Tree come output) inserendo dopo la definizione di un’alternativa un blocco di codice Java (o comunque nel linguaggio target) racchiuso tra partentesi graffe: returnNode : ^(’return’ expr) { System.out.println("Nodo return"); } ; Questa regola, ogni qualvolta si incontri un nodo return, produrr` una a stampa a video della stringa "Nodo return". Una volta scritte e combinate tra loro tutte le regole necessarie, se il file contenente la grammatica si chiamasse TWalker.g, ANTLR produrrebbe il file TWalker.java, necessario al riconoscimento effettivo degli AST la cui struttura ` conforme alla grammatica; questo file rappresenta e quello che in precedenza ` stato chiamato tree parser. e 2.4 Abstract Syntax Tree Un Abstract Syntax Tree ` una rappresentazione ad albero della struttura e sintattica di un codice sorgente scritto in un particolare linguaggio di programmazione. Ogni nodo dell’albero denota un costrutto presente nel codice sorgente, sebbene non ne rappresenti fedelmente la sintassi, ma solo le parti essenziali. Ad esempio, le parentesi graffe che racchiudono un blocco di codice nei linguaggi C-like sono implicite in un AST, e non vi vengono rappresentate. A titolo esemplificativo, sia dato il seguente codice sorgente JavaScript: 1 2 3 4 5 6 7 8 9 function prova (a , b ) { var c ; if ( a +b >9) { c =2; } else { c =0; } return c ; } Il corrispondente AST, generato a partire dalla grammatica TinyJavaScript, ` quello riportato in figura 2.1. e
  • 16. 2. Scenario 10 Figura 2.1: AST corrispondente al codice JavaScript dell’esempio. Il nodo nil rappresenta la radice4 dell’albero, il nodo SLIST denota una successione di statement; tutti gli altri nodi hanno un significato immediato. 4 Secondo la convenzione adottata da ANTLR.
  • 17. Capitolo 3 Progettazione La fase progettuale mette in chiaro come si sia scelto di affrontare il passaggio da un codice JavaScript ad un altro che differisca dal primo solo per una singola modifica, nel senso che verr` definito pi` avanti. a u 3.1 Specifiche del problema Il problema affrontato prevede di analizzare una singola funzione JavaScript (costituita da un insieme di statement di numerosit` arbitraria) a e di scambiare le posizioni di due parti di codice scelte “a caso”; nei paragrafi successivi si tratteranno approfonditamente i dettagli di queste operazioni. 3.2 Manipolazione di codice sorgente Le manipolazioni nel codice sorgente che verranno qui considerate consistono in scambi casuali di nodi dell’AST corrispondente. Per fare ci` ` necessario prima analizzare il codice, costruirne il reoe lativo AST, eseguire gli scambi e visitare l’AST mutato per generare il nuovo codice sorgente. 3.2.1 Da codice sorgente ad AST Il passaggio da codice sorgente ad AST richiede come prerequisito la stesura della grammatica necessaria al riconoscimento dei costrutti JavaScript; a tale scopo ` stata scritta la grammatica TinyJavaScript nel file e TinyJavaScript.g utilizzando ANTLRWorks (par. 4.1.1); analizziamo brevemente il suo contenuto. 11
  • 18. 12 3. Progettazione La prima sezione, denominata options, contiene dei settaggi che ANTLR utilizzer` per generare il lexer ed il parser (par. 2.3.1): a 1 2 3 4 5 options { output = AST ; ASTLabelType = CommonTree ; backtrack = true ; } In particolare, l’opzione output=AST; dice ad ANTLR che l’output del parser sia l’AST corrispondente al codice sorgente ispezionato. Segue poi la definizione di alcuni token immaginari (par. 2.3.2) che verranno impiegati all’interno della grammatica: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 tokens { VAR ; FUNC ; ARG ; NEW ; CALL ; INDEX ; SLIST ; FOR ; COND ; ITERATE ; WHILE_DO ; DO_WHILE ; IF ; THEN ; ELSE ; SWITCH ; DEFAULT ; RETURN ; } // // // // // // // // // // // // // // // // // // variable definition function definition formal argument new object function call array index statement list for cycle condition in for cycle iteration in for cycle while cycle do .. while cycle if statement then block else block switch statement switch default case return statement TinyJavaScript.g contiene sia le regole del lexer (normalmente denotate con lettere maiuscole) sia quelle del parser. Prendiamo in esame una delle regole del lexer: 1 2 3 ID : ( ’a ’.. ’ z ’| ’A ’.. ’ Z ’| ’_ ’) ( ’a ’.. ’ z ’| ’A ’.. ’ Z ’| ’0 ’.. ’9 ’| ’ _ ’) * ; Questa regola consente di riconoscere tutte le stringhe di caratteri che inizino con una lettera minuscola o maiuscola oppure col carattere under-
  • 19. 13 Manipolazione di codice sorgente score e a cui seguono, eventualmente (l’asterisco al termine del secondo gruppo significa appunto che quel gruppo ` opzionale), altre lettere mae iuscole o minuscole oppure numeri oppure caratteri underscore. Qualsiasi altro carattere (punteggiatura, parentesi, ...) non viene preso in considerazione da questa regola. Una regola del parser ` invece ad esempio questa: e 1 2 3 4 ifStat : ’if ’ ’( ’ cond = condStat ’) ’ mthen = stat ( ’ else ’ melse = stat ) ? -> ^( IF ^( COND $cond ) ^( THEN $mthen ) ^( ELSE $melse ?) ) ; Si tratta, com’` intuibile, della regola per il riconoscimento di costrutti e if-then-else, con l’accorgimento che il ramo else pu` essere assente. o I caratteri “->” indicano che l’AST generato deve avere la struttura specificata come segue: il nodo padre ` IF seguito dai figli COND, THEN e e ELSE. Ciascuno di essi ` a loro volta padre di altri sotto-alberi, eccetto e il nodo ELSE, il quale pu` non avere figli se nel corrispondente codice o JavaScript non ` presente il blocco else. e Il parsing del codice inizia dalla regola program mediante le istruzioni 1 2 3 4 5 6 ANTLRFileStream stream = new ANTLRFileStream ( " BubbleSort . js " ) ; lexer = new T i n yJ a v aS c r ip t L ex e r ( stream ) ; tokens = new Co mmonTo kenStr eam ( lexer ) ; T i n y J a v a S c r i p t P a r s e r parser = new T i n y J a v a S c r i p t P a r s e r ( tokens ) ; T i n y J a v a S c r i p t P a r s e r . program_return r = parser . program () ; tree = ( CommonTree ) r . getTree () ; Una volta terminato il parsing, la variabile tree contiene l’AST generato. 3.2.2 Manipolazione di AST La manipolazione dell’AST inizia specificando quanti scambi di nodi devono essere eseguiti; supponiamo siano in numero n. Si esegue dunque un ciclo for avente n iterazioni; ad ogni iterazione si compiono le operazioni seguenti:
  • 20. 3. Progettazione 14 • Si inizia un ciclo while, eseguito fintantoch´ non venga prodotta e una mutazione valida; ogni iterazione di questo ciclo prevede le seguenti istruzioni: – Vengono generati due numeri casuali interi non negativi k1 e k2 che assumono come valore minimo 1 e massimo p, con p numero totale di sotto-alberi; tali numeri costituiscono gli indici dei nodi che verranno selezionati – Si copia l’AST attuale per consentirne il ripristino in caso di scambio non valido – Si esegue lo scambio del nodo k1 col nodo k2 – Si verifica la legittimit` dello scambio; se lo ` si esce dal a e ciclo while e si procede con la successiva iterazione del ciclo for esterno, altrimenti si ripristina l’AST alla situazione precedente allo scambio e si ripetono i passi del ciclo while • A mutazione avvenuta, si visita l’AST per generarne il codice sorgente JavaScript (paragrafo successivo) e viene scritto il risultato su un file avente nome ModBubble*.js, con * numero progressivo che va da 0 a n − 1 • Al termine del ciclo for la variabile tree contiene la versione mutata della funzione JavaScript originale Il codice Java che implementa quanto descritto ` e 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 CommonTree sree ; int index = 0; for ( int i =0; i <n ; i ++) { boolean changed = false ; while (! changed ) { count = 0; trees . clear () ; visitTree ( tree ) ; k1 = random . nextInt ( count ) ; k2 = random . nextInt ( count ) ; sree = copyTree ( tree ) ; swapNodes ( trees . get ( k1 ) , trees . get ( k2 ) ) ; changed = ( sree . toStringTree () . equals ( tree . toStringTree () ) ? false : true ) ; } writeCodeToFile ( " ModBubble " + index + " . js " ) ; index ++; }
  • 21. 15 Manipolazione di codice sorgente Si noti che la presenza del ciclo while assicuri che vengano eseguiti esattamente n scambi. La funzione visitTree(t) ispeziona l’AST per calcolarne il numero totale di sotto-alberi e, per completezza, li inserisce nell’ArrayList chiamato trees (definito all’inizio della classe): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void visitTree ( CommonTree t ) { if ( t . getChildCount () > 0) { for ( int i =0; i <t . getChildCount () ; i ++) { if ( down ) { count ++; trees . add ( t ) ; } down = true ; visitTree (( CommonTree ) t . getChild ( i ) ) ; } } else { count ++; trees . add ( t ) ; down = false ; } } Il metodo writeCodeToFile(s) verr` discusso nel paragrafo che sea gue. 3.2.3 Da AST a codice sorgente Il passaggio da AST a codice sorgente avviene visitando l’albero utilizzando l’algoritmo Depth-First Search 1 . Si parte dal nodo denominato program e si ispezionano i nodi figli, da sinistra verso destra. Ad ogni visita di un nodo si stampa il suo contenuto e si passa all’ispezione dei figli; al raggiungimento del nodo foglia per quel sotto-albero, si passa ad ispezionare il nodo fratello successivo a quello che si era preso in esame. Si procede in questo modo fino a quando tutti i nodi dell’AST siano stati ispezionati. Una volta terminata la generazione del codice JavaScript, si scrive il risultato su un file il cui nome viene specificato dal parametro del metodo writeCodeToFile(s). 1 2 public static void writeCodeToFile ( String name ) { C o de S t ri n g Ge n e ra t o r cg = new C o de S t ri n g Ge n e ra t o r ( tree ) ; 1 http://en.wikipedia.org/wiki/Depth-first search
  • 22. 3. Progettazione cg . visitTree ( tree ) ; cg . writeCodeToFile ( name ) ; 3 4 5 16 } La classe CodeStringGenerator contiene tutti i metodi necessari alla generazione del codice a partire dall’ispezione dell’AST; tra di essi vi ` e writeCodeToFile(s), il cui listato ` riportato qui sotto: e 1 2 3 4 5 6 7 8 9 10 11 public boolean writeCodeToFile ( String fileName ) { if ( code . length () ==0) visitTree ( tree ) ; try { FileWriter fw = new FileWriter ( fileName , false ) ; fw . write ( code ) ; fw . close () ; return true ; } catch ( Exception e ) { return false ; } } Come si vede, se la scrittura ` andata a buon fine, il metodo restituisce e il valore booleano true, altrimenti restituisce false.
  • 23. Capitolo 4 Implementazione 4.1 Impiego di ANTLR Il tipico impiego di ANTLR consiste nelle seguenti tre macro-operazioni: 1. Uso del lexer e del parser in serie per analizzare il flusso di dati in ingresso, controllarne la conformit` alle grammatiche sviluppate e, a se non sono stati incontrati errori, generare una rappresentazione intermedia come l’AST; 2. Opzionalmente, modificare l’AST per mezzo di uno o pi` Tree u Parser (par. 2.3.1); 3. Produrre l’output finale utilizzando un Tree Parser, ad esempio per generare codice sorgente modificato. 4.1.1 ANTLRWorks ANTLRWorks (http://www.antlr3.org/works/) ` un IDE per la stesura e di grammatiche ANTLR scritto in Java, originariamente sviluppato da Jean Bovet in collaborazione con il prof. Terence Parr. Sebbene attualmente sia disponibile la versione 2.1 che supporta pienamente le specifiche di ANTLR 4, si ` qui preferito utilizzare la versioe ne 1.5 in quanto ` stata specificatamente studiata per ANTLR 3.x (la e versione di ANTLR utilizzata nel progetto ` la 3.5). e ANTLRWorks fornisce funzionalit` quali evidenziazione della sintasa si, creazione dell’albero delle regole, ricerca mediante espressioni regolari, refactoring, suggerimenti in tempo reale, generazione del diagramma decisionale, interprete interattivo per il testing delle regole, visualizzazione grafica di AST, esportazione su file dei diagrammi generati, debugger e molto altro ancora. 17
  • 24. 4. Implementazione 18 Seguono alcuni screenshot dell’applicazione: Figura 4.1: Scrittura di regole ANTLR. Figura 4.2: Funzionalit` di debugging. a 4.2 Ordinamento di un vettore Come algoritmo su cui eseguire le manipolazioni che verranno pi` avanti u descritte, ` stato scelto un algoritmo di ordinamento di un vettore; nello e specifico, ` stato considerato BubbleSort. e
  • 25. 19 4.2.1 Ordinamento di un vettore BubbleSort L’algoritmo BubbleSort (letteralmente “ordinamento a bolla”) funziona in modo piuttosto intuitivo: dato un vettore di n numeri, si inizia a confrontare il primo elemento con il successivo; se il primo risulta essere maggiore del secondo allora avviene uno scambio di posizione, altrimenti no. Si procede poi confrontando il secondo con il terzo elemento, si riesegue il confronto ed eventualmente si invertono le posizioni; si ripete questo procedimento sino al confronto tra il penultimo e l’ultimo elemento (dunque n − 1 volte), dopodich´ si ricomincia dalla prima coppia del e vettore. Si continuano ad eseguire confronti lungo tutta la lunghezza del vettore sino a quando non sono stati eseguiti n(n − 1) confronti totali. Per meglio spiegarne l’idea, si ricorre ad un esempio: si immagini di avere in mano n carte da gioco francesi e si supponga di volerle ordinare. A tale scopo, si confronti la prima carta con la seconda: se la prima ` e maggiore della seconda allora la prima carta prende il posto della seconda e viceversa, altrimenti non avviene alcuno scambio. Si proceda poi con la seconda e la terza carta e si ripeta il confronto; l’intero procedimento va ripetuto sino all’ultima carta in mano. A questo punto si avr` una successione di carte non ancora completamente ordinata; per a raggiungere una situazione di ordinamento globale ` necessario rieseguire e il procedimento descritto sino a quando la mano di carte risulti ordinata. Un limite superiore al numero di confronti che ` necessario eseguire per e ordinare n carte con questo metodo ` appunto n(n − 1). e 4.2.1.1 Pseudo-codice di BubbleSort Si riporta di seguito lo pseudo-codice dell’algoritmo BubbleSort: bubblesort(v): u = v n = length(v) for i=1 to n do for j=1 to n-1 do if u[j]>u[j+1] then k = u[j] u[j] = u[j+1] u[j+1] = k return u Come si nota, tale algoritmo non modifica il vettore v originale, ma produce una sua copia ordinata. In realt`, nella presente tesi ` stata utilizzata una versione modifia e cata della versione elementare di BubbleSort qui esposta: in effetti, non
  • 26. 4. Implementazione 20 sempre ` necessario eseguire n(n − 1) iterazioni prima di giungere all’ore dinamento completo; se si introduce una variabile sentinella che segnali l’assenza di scambi in un’iterazione del secondo ciclo for (e dunque si ` e giunti prematuramente all’ordinamento), ` possibile interrompere i cicli e e restituire direttamente il vettore u. Lo pseudo-codice di questa versione ottimizzata di BubbleSort ` il e seguente: smart_bubblesort(v): u = v n = length(v) for i=1 to n do flag = false for j=1 to n-1 do if u[j]>u[j+1] then k = u[j] u[j] = u[j+1] u[j+1] = k flag = true if flag==false then return u return u 4.3 Prove eseguite Il linguaggio di programmazione scelto per l’implementazione di BubbleSort ` JavaScript. e Nel file BubbleSort.js ` stato memorizzato il seguente codice: e 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function bubbleSort ( array ) { var ordered = copyArray ( array ) ; for ( var i =0; i < ordered . length ; i ++) { var flag = false ; for ( var j =0; j < ordered . length -1; j ++) { if ( ordered [ j ]) { var k = ordered [ j ]; ordered [ j ] = ordered [ j +1]; ordered [ j +1] = k ; flag = true ; } } if (! flag ) { break ; } }
  • 27. 21 return ordered ; 17 18 Prove eseguite } ` E questo il file di partenza che viene modificato dal cuore del programma (par. 3.2.2). Per interfacciarsi alle funzionalit` richieste ` stata creata una pagina a e HTML divisa in varie sezioni. La prima sezione contiene un form attraverso cui specificare le caratteristiche dei vettori da generare casualmente; per casualmente si intende che ciascuno di essi ` costituito da un numero casuale di elementi come presi tra un valore minimo e uno massimo specificati dall’utente e che ogni elemento dei vettori generati ` scelto a caso, con il vincolo che non e vi siano ripetizioni di elementi all’interno dello stesso vettore. Figura 4.3: Form di generazione casuale dei vettori. La seconda sezione consente di ordinare i vettori generati con l’algoritmo BubbleSort originale, ovvero non ancora mutato. La terza sezione contiene una drop area in cui trascinare i file contenenti le versioni mutate di BubbleSort. Infine, la quarta ed ultima sezione contiene alcuni grafici relativi alle performance degli algoritmi eseguiti; i dettagli verranno discussi nel capitolo relativo all’analisi dei risultati. 4.3.1 Generazione casuale di vettori Il primo step per la valutazione delle prestazioni degli algoritmi prodotti consiste nella generazione dei vettori su cui poi lavorare. Una volta specificati il numero di vettori da produrre, il numero minimo e massimo degli elementi di ciascuno di essi e il massimo valore che ogni elemento pu` assumere (inteso come valore assoluto: specio ficando 100 come valore massimo, i valori possibili saranno contenuti
  • 28. 4. Implementazione 22 Figura 4.4: Output dei vettori generati. Figura 4.5: Ordinamento dei vettori con BubbleSort non modificato. Figura 4.6: Inserimento dei file contenenti versioni mutate di BubbleSort. nell’intervallo discreto [−100, 100]), la funzione che genera i vettori ` la e seguente:
  • 29. 23 1 2 3 4 5 6 7 Prove eseguite function genRandMatrix ( rows , colsMin , colsMax , valMax ) { var matrix = new Array ( rows ) ; for ( var i =0; i < rows ; i ++) { matrix [ i ] = genRandArray ( colsMin , colsMax , valMax ) ; } return matrix ; } La funzione genRandArray(a,b,c) produce un vettore che ha tra a e b elementi e ciascuno di essi ` compreso tra -c e +c; ` cos` costituita: e e ı 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function genRandArray ( colsMin , colsMax , valMax ) { var l = posRand ( colsMin , colsMax ) ; var array = new Array ( l ) ; var rand ; var present ; for ( var i =0; i <l ; i ++) { present = true ; rand = random ( valMax ) ; while ( present && i > 0) { for ( var j =0; j <i ; j ++) { if ( array [ j ]== rand ) { rand = random ( valMax ) ; break ; } else { if ( j == i -1) present = false ; } } } array [ i ] = rand ; } return array ; } La funzione posRand(a,b) genera un numero intero casuale compreso tra a e b; il ciclo while e la variabile booleana present sono necessarie per assicurarsi che non vi siano elementi ripetuti nel vettore generato; la funzione random(a) genera un numero intero casuale compreso tra -a e +a. 4.3.2 Ordinamento con BubbleSort originale Dopo che i vettori siano stati generati, si procede al loro ordinamento con la versione originale di BubbleSort; si ottiene come risultato una copia
  • 30. 4. Implementazione 24 correttamente ordinata dei vettori di partenza. Si noti che i vettori originali non vengono eliminati: dovranno infatti essere ordinati dalle versioni modificate di BubbleSort. 4.3.3 Modifica di BubbleSort Partendo dal file BubbleSort.js che contiene il codice JavaScript di BubbleSort originale, si esegue il programma sviluppato per la generazione di n versioni mutate di BubbleSort; il funzionamento di tale programma ` sintetizzabile nei seguenti passaggi: e • Analizza il file BubbleSort.js e dal codice sorgente genera l’AST (par. 3.2.1) • Esegue n volte quanto segue: – Sceglie a caso due nodi dell’albero – Verifica se lo scambio ` fattibile (i due nodi non devono essere e l’uno l’antenato dell’altro e non devono essere lo stesso nodo) – Esegue un tentativo di scambio; se lo scambio ` andato a e buon fine (nel senso che il nuovo albero ` conforme alla tree e grammar) allora si procede al prossimo scambio, altrimenti si ripetono i due passi precedenti fintantoch´ non venga eseguito e uno scambio valido (par. 3.2.2) – L’AST mutato viene ritrasformato in codice JavaScript ed il risultato viene scritto nel file ModBubble*.js, dove al posto del carattere * vi ` un numero progressivo compreso tra 0 e e n − 1 (par. 3.2.3) • Al termine del ciclo, tutte le mutazioni sono state scritte su file; il programma ` stato studiato appositamente per avere la certezza e che in ogni caso vi siano esattamente n mutazioni Il risultato dell’esecuzione del programma appena descritto ` una suce cessione di n file, ciascuno contenente una funzione BubbleSort che differisce da quella contenuta nel file immediatamente precedente e successivo solamente per una modifica al corrispondente AST. 4.3.4 Ordinamento con BubbleSort modificato L’ordinamento dei vettori con le versioni mutate di BubbleSort inizia trascinando tutti i file ModBubble*.js all’interno della drop area nella pagina HTML descritta sopra.
  • 31. 25 Prove eseguite La drop area di figura 4.6 ` stata ottenuta mediante l’inserimento e di due tag <div> annidati; il primo funge da contenitore, il secondo ` e associato agli eventi necessari alla cattura dei file: 1 2 3 < div id = " drop_container " > < div id = " drop_zone " > Drop Algorithms Here ! </ div > </ div > Gli eventi necessari alla cattura dei file sono due: handleDragOver e handleFileSelect. Il primo serve a rendere l’effetto visivo della disponibilit` ad accogliere file durante il loro trascinamento nella drop area, a il secondo si occupa effettivamente di leggerli e di eseguire le versioni di BubbleSort in essi contenute. L’associazione degli eventi alla drop area avviene per mezzo delle istruzioni sottostanti: 1 2 3 var dropZone = document . getElementById ( ’ drop_zone ’) ; dropZone . addEventListener ( ’ dragover ’ , handleDragOver , false ) ; dropZone . addEventListener ( ’ drop ’ , handleFileSelect , false ) ; Il contenuto della funzione handleDragOver() ` il seguente: e 1 2 3 4 5 function handleDragOver ( evt ) { evt . stopPropagation () ; evt . preventDefault () ; evt . dataTransfer . dropEffect = ’ copy ’; } Il contenuto della funzione handleFileSelect() ` invece questo: e 1 2 3 function handleFileSelect ( evt ) { evt . stopPropagation () ; evt . preventDefault () ; 4 5 6 7 8 9 var files = evt . dataTransfer . files ; perfs = new Array ( files . length ) ; bubbles = new Array ( files . length ) ; fnames = new Array ( files . length ) ; w = 0; 10 11 12 strPerfIndexes = " <h3 > PERFORMANCE INDEXES </ h3 > " ; var txtPerfMatrix = document . getElementById ( " txtPerfMatrix " ) ;
  • 32. 4. Implementazione 26 setHidden ( txtPerfMatrix , false ) ; 13 14 for ( var i =0 , f ; f = files [ i ]; i ++) { if (! f . type . match ( ’ javascript ’) ) { continue ; } var reader = new FileReader () ; fnames [ i ] = f . name ; reader . onload = loaded ; reader . readAsText ( f ) ; } 15 16 17 18 19 20 21 22 23 24 } Brevemente, la variabile files ` di tipo FileList, un vettore di e oggetti File i quali sono a loro volta interfacce ai file selezionati. Queste funzionalit` sono messe a disposizione dalla File API di HTML5 (http: a //www.w3.org/TR/FileAPI/). All’interno del ciclo for viene innanzitutto verificato che il file attuale sia effettivamente un sorgente JavaScript; se lo ` si procede con le e istruzioni successive, altrimenti si passa ad analizzare il prossimo file (se c’`). e L’oggetto reader, istanziazione di FileReader, mette a disposizione la funzionalit` di lettura del contenuto del file. a L’istruzione reader.onload = loaded; serve per lanciare la funzione loaded() una volta chiamata reader.readAsText(f);. La funzione loaded() ` proprio quella che estrae il contenuto del file, e lo valuta, ne crea un oggetto function e la applica ai vettori da ordinare: 1 2 3 function loaded ( evt ) { var fileString = evt . target . result ; bubbles [ w ] = fileString ; 4 5 6 7 8 9 10 11 12 13 14 15 16 try { newBubbleSort = eval ( " ( " + fileString + " ) " ) ; ordermatrix = matrixOrder ( omatrix ) ; var perfArrays = new Array ( rows ) ; for ( var j =0; j < rows ; j ++) { perfArrays [ j ]= perfIndexArray ( bubblematrix [ j ] , ordermatrix [ j ]) ; } perfs [ w ] = globalPerfIndex ( perfArrays ) ; } catch ( e ) { perfs [ w ] = null ; } finally { strPerfIndexes += fnames [ w ]+ " : <b > " + perfs [ w ]+ " </b > < br / > " ;
  • 33. 27 w ++; txtPerfMatrix . innerHTML = strPerfIndexes ; 17 18 } 19 20 Prove eseguite } Viene poi calcolato l’indice di prestazione (par. 4.3.5) relativo a quella mutazione di BubbleSort e viene scritto sulla pagina HTML (riga 18) sotto la drop area. Figura 4.7: Output degli indici di prestazione. 4.3.5 Indici di prestazione L’indice di prestazione tra due vettori costituiti dagli stessi elementi ` qui e definito come media aritmetica delle reciproche distanze tra la posizione occupata dall’i-esimo elemento nel primo vettore e la posizione occupata dallo stesso elemento nel secondo vettore: k= 1 n d(i, j) i,j Supponendo che il primo vettore sia v = [3, 1, 2] e che il secondo sia u = [1, 2, 3], si procede al calcolo del vettore w, in cui l’i-esimo elemento rappresenta il numero di posizioni per cui differisce v[i] dallo stesso elemento all’interno di u. Nel caso in esame, il numero 3 occupa la prima posizione in v e in u occupa la terza posizione; la distanza vale dunque w[1] = |3 − 1| = 2. Completando il calcolo delle distanze ai due elementi rimanenti, si ottiene w = [2, 1, 1], il quale genera un indice di prestazione k pari a (2 + 1 + 1)/3 = 4/3 1, 33.
  • 34. 4. Implementazione 28 Si noti che nel caso in cui k = 0 allora i due vettori sono ordinati allo stesso modo (e quindi vi ` massima similitudine), mentre all’aumentare e di k aumenta il grado di mutazione nelle posizioni degli elementi. Detta n la lunghezza dei vettori da confrontare, un limite superiore per k ` k = n/2 se n ` pari, mentre ` k = 2 (n − l)/n con l = (n−1), (n− e e e 3), · · · , 2 se n ` dispari (situazioni che corrisponderebbero all’inversione e totale delle posizioni degli elementi tra il primo ed il secondo vettore); da queste considerazioni, si pu` affermare che o 0≤k≤ n 2 (n − l)/n se n pari se n dispari Non vi sono situazioni di ambiguit` nel calcolo dell’indice k poich´, a e per come sono stati generati i vettori di partenza (par. 4.3.1), nessuno di essi contiene elementi ripetuti. Ora che ` stato definito l’indice di prestazione relativo ad una singola e coppia di vettori, si pu` introdurre la definizione di indice globale di o prestazione, denotato con K: esso rappresenta la media aritmetica degli indici di prestazioni kr relativi a tutti i vettori generati: 1 K= n n kr r=1 Questo indice ` significativo in quanto, data una mutazione τ di e BubbleSort originale, K rappresenta sinteticamente la correttezza del risultato rispetto a quello desiderato. Diremo che τ ` una mutazione equivalente se, per ogni vettore d’ine gresso, il risultato prodotto ` uguale a quello di BubbleSort originale. In e tal caso l’indice globale di prestazione Kτ associato a τ vale 0. Supponiamo, per maggiore chiarezza, che la mutazione τ sia tale che i vettori da esso generati siano ordinati secondo un ordinamento decrescente1 (ovvero il contrario di quanto faccia BubbleSort originale). Partendo dai vettori x1 = [1, −3, −6] x2 = [1, 0, 2] x3 = [−6, 7] l’esecuzione di BubbleSort originale su di essi produrrebbe i vettori y1 = [−6, −3, 1] y2 = [0, 1, 2] y3 = [−6, 7] mentre l’esecuzione di τ produrrebbe z1 = [1, −3, −6] 1 z2 = [2, 1, 0] z3 = [7, −6] Si tratta di una situazione prettamente esemplificativa, in quanto una simile mutazione ` impossibile nel presente lavoro (qui si considerano mutazioni costituite da e scambi di nodi dell’AST).
  • 35. 29 Prove eseguite Per valutare la “bont`” di τ (nel senso di cui sopra) si procede dunque a al calcolo degli indici kr : k1 = (2+0+2)/3 1, 33 k2 = (2+0+2)/3 1, 33 k3 = (1+1)/2 = 1 e dunque l’indice di prestazione globale associato a τ vale Kτ = (k1 + k2 + k3 )/3 = (1, 33 + 1, 33 + 1)/3 1, 22 Pu` capitare, e anzi ` una situazione piuttosto frequente, che per o e qualche τ il risultato della sua esecuzione non sia un vettore: a tal proposito si veda il paragrafo 5.1.2. L’algoritmo per il calcolo dei kr ` il seguente: e 1 2 3 function perfIndexArray ( a1 , a2 ) { return Math . round ( sumArray ( rankArray ( a1 , a2 ) ) / a1 . length *100) /100; } dove sumArray(v) restituisce la somma degli elementi del vettore v, mentre la funzione rankArray(a,b) ` definita in questo modo: e 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function rankArray ( a1 , a2 ) { var matched ; var distances = new Array ( a1 . length ) ; for ( var i =0; i < a1 . length ; i ++) { matched = false ; for ( var j =0; j < a2 . length && ! matched ; j ++) { if ( a1 [ i ]== a2 [ j ]) { distances [ i ] = Math . abs (i - j ) ; matched = true ; } } } return distances ; } Per quanto riguarda infine il calcolo dell’indice globale di prestazione Kτ , l’algoritmo che lo calcola ` il seguente: e 1 2 3 function globalPerfIndex ( perfArray ) { return Math . round ( sumArray ( perfArray ) / perfArray . length *100) /100; } dove il vettore perfArray contiene tutti gli indici kr .
  • 36.
  • 37. Capitolo 5 Risultati In questo capitolo verranno discussi i risultati ottenuti a fronte dell’esecuzione degli esperimenti. 5.1 Situazioni possibili Si esamineranno nel seguito le situazioni che si possono verificare dopo l’esecuzione di una mutazione di BubbleSort originale. 5.1.1 Ordinamento corretto Il caso di ordinamento corretto si verifica dopo l’esecuzione dell’algoritmo BubbleSort originale o di una sua mutazione equivalente1 τeq . Come risultato, si avranno dei vettori correttamente ordinati in senso crescente, degli indici di prestazione kr tutti nulli e un indice globale di prestazione associato all’algoritmo in esame Kτ anch’esso nullo. 5.1.2 Ordinamento con mutazione non equivalente Se la mutazione τ di BubbleSort non ` equivalente, vi sono le seguenti e situazioni mutualmente esclusive: • Errore a runtime: τ produce un errore durante la sua esecuzione che ne provoca la prematura terminazione • Output diverso da vettore: τ viene eseguito senza intoppi ma l’oggetto restituito non ` un vettore e • Output ` un vettore: τ viene eseguito senza intoppi e l’oggetto e restituito ` un vettore non correttamente ordinato e 1 Si veda il paragrafo 4.3.5. 31
  • 38. 5. Risultati 32 • Ciclo infinito: τ resta bloccato in un ciclo infinito e non termina mai I primi due casi, seppure rappresentino l’esito della maggioranza degli esperimenti, hanno l’effetto di generare degli indici kr,τ (intendendo con tale notazione l’indice di prestazione riferito al vettore r dopo l’esecuzione di τ ) e Kτ non ben definiti; si ` scelto quindi di comportarsi nei seguenti e modi: • Errore a runtime: kr,τ = null e Kτ = null • Output diverso da vettore: kr,τ = NaN e Kτ = NaN Il caso invece di corretta esecuzione di τ e di output di un vettore (inevitabilmente non correttamente ordinato data l’ipotesi di mutazione non equivalente) presenter` degli indici di prestazione globali e non gloa bali tutti diversi da zero; l’unica situazione in cui essi sono nulli ` che e tutti i vettori da ordinare siano costituiti da un solo elemento (situazione banale). Tali indici saranno tanto pi` elevati tanto pi` diverso dalla situazione u u ideale sar` l’ordinamento degli elementi dei vettori prodotti. a Infine, nel caso di ciclo infinito, non viene prodotto alcun indice di prestazione, dato che ` necessario forzare la terminazione del programma. e 5.2 Analisi dei risultati Per avere un quadro suffucientemente ampio dei risultati ottenuti, sono stati condotti due esperimenti; il primo producendo 1000 mutazioni di BubbleSort, il secondo producendone 5000. Per ogni esperimento sono stati elaborati due grafici: • Grafico di tentativi di scambio per mutazione: mostra quanti tentativi di scambio si sono resi necessari per passare dalla mutazione τi alla mutazione τi+1 ; in ascisse ` riportato il numero di e mutazione, mentre in ordinate i corrispondenti numeri di scambi richiesti • Grafico di errori a runtime: mostra quante mutazioni hanno prodotto un errore a runtime e quante invece hanno terminato correttamente Un terzo grafico, detto grafico degli indici globali di prestazione, che mostra l’andamento degli indici Kτ al variare della mutazione τ , non ` e stato incluso nel presente lavoro poich´ non fornisce alcuna informazione e significativa a causa dei risultati ottenuti.
  • 39. 33 Analisi dei risultati Per entrambi gli esperimenti sono stati presi in esame 30 vettori, ciascuno dei quali costituito da un numero di elementi variabile da 10 a 30 e ogni elemento pu` assumere valori interi nel range [−100, 100]. o 5.2.1 1000 mutazioni Si analizza per prima cosa il numero di tentativi di scambio per mutazione: dai dati raccolti si osserva che si va da un minimo di 1 tentativo di scambio ad un massimo di 34 tentativi, con una media di 5,14 tentativi/mutazione. 50 40 30 20 10 0 0 200 400 600 800 1000 Per quanto riguarda l’esito dell’esecuzione degli algoritmi mutati, ` e emerso che 968/1000 hanno prodotto errori a runtime terminando prematuramente, 31/1000 sono stati interamente eseguiti e 1/1000 presentava un ciclo infinito che ha richiesto la terminazione forzata del programma.
  • 40. 34 5. Risultati 1000 900 800 700 600 500 400 300 200 100 0 Non eseguiti Eseguiti Cicli infiniti Delle 31 mutazioni che hanno terminato correttamente la propria esecuzione, solo 2 hanno restituito vettori; si tratta in particolare delle prime 2 mutazioni: Mutazione τ0 τ1 τ2 ··· τ999 Indice Kτ 6,29 6,30 null null o NaN null Tabella 5.1: Indici globali di prestazione nel primo esperimento. Un indice Kτ pari a circa 6 significa che mediamente ogni vettore ordinato da quella mutazione presenta elementi distanti 6 posizioni da quelle che dovrebbero assumere nel caso di ordinamento corretto. 5.2.2 5000 mutazioni In questo secondo esperimento il numero di tentativi di scambio per mutazione sono i seguenti: si va da un minimo di 1 tentativo di scambio ad un massimo di 45 tentativi, con una media di 25,92 tentativi/mutazione.
  • 41. 35 Analisi dei risultati 50 40 30 20 10 0 0 1000 2000 3000 4000 5000 L’esito dell’esecuzione degli algoritmi mutati ha evidenziato che 4923/5000 hanno terminato prematuramente a causa di errori a runtime, 49/5000 sono stati eseguiti per intero e i rimanenti 28/5000 non terminavano a causa di cicli infiniti. 5000 4500 4000 3500 3000 2500 2000 1500 1000 500 0 Non eseguiti Eseguiti Cicli infiniti Delle 49 mutazioni che hanno correttamente terminato la propria esecuzione, nessuna di esse ha restituito vettori e dunque gli indici globali di prestazione assumono tutti o valori null oppure NaN:
  • 42. 36 5. Risultati Mutazione τ0 τ1 τ2 ··· τ999 Indice Kτ null null null null o NaN null Tabella 5.2: Indici globali di prestazione nel secondo esperimento. Ci` significa che ogni mutazione priva di cicli infiniti non produce o vettori, per ogni input. 5.2.3 Confronto tra i risultati Nel primo esperimento, quello che coinvolge 1000 mutazioni di BubbleSort, si sono evidenziati risultati pi` soddisfacenti rispetto al secondo u esperimento, sia in termini di numero medio di tentativi di scambio necessari per passare dalla mutazione τi alla mutazione τi+1 (5,14 contro 25,92), sia in termini di numero di mutazioni che hanno prodotto vettori (2 contro 0). La seguente tabella riassume la situazione: Mutazioni coinvolte Media scambi/mutazione Mutazioni con errori a runtime Mutazioni eseguite interamente Mutazioni con cicli infiniti Mutazioni con output vettore Esperimento 1 1000 5,14 96,80% 3,10% 0,10% 0,20% Esperimento 2 5000 25,92 98,46% 0,98% 0,56% 0,00% Tabella 5.3: Comparazione dei risultati. In ultima analisi, all’aumentare del numero di mutazioni generate diminuisce la percentuale di mutazioni che producono vettori (solitamente solo le prime lo fanno). Non ci si deve stupire del fatto di aver ottenuto una cos` bassa perı centuale di mutazioni pseudocorrette: le modifiche successive effettuate agli AST sono di natura casuale e consistono nello scambio di nodi; ci` si o traduce in un codice sorgente che inevitabilmente subisce grandi modifiche anche per (apparentemente) piccole mutazioni (si pensi ad esempio allo scambio tra la primissima istruzione e lo statement return).
  • 43. Capitolo 6 Conclusioni L’obiettivo di questo lavoro di tesi ` stato quello di studiare gli effetti e prodotti da una serie di mutazioni successive nel codice di una funzione JavaScript sui dati d’ingresso. Quanto esposto nei capitoli precedenti permette di affermare che tale obiettivo sia stato pienamente raggiunto. Il software sviluppato permette, infatti, di eseguire un numero a piacere di mutazioni su una funzione JavaScript memorizzata su file che viene chiesta in ingresso; a partire da questa, il software esegue tutte le elaborazioni necessarie e, oltre ai file contenenti le versioni modificate della funzione originale, produce inoltre una statistica circa il numero di tentativi di scambio eseguiti per ogni mutazione effettiva. Un secondo software sviluppato in JavaScript si occupa poi di generare i vettori su cui eseguire i test, di prendere in ingresso l’insieme dei file contenenti gli algoritmi mutati e di generare gli indici globali di prestazione. 6.1 Sviluppi futuri Una possibile estensione di quanto qui svolto potrebbe essere quella di considerare un numero maggiore di mutazioni e lavorare su pi` funzioni u iniziali, al fine di giungere ad un probabile miglioramento degli indici globali di prestazione. Un’altra strada percorribile che richiede, almeno in prima battuta, la sola scrittura di nuove grammatiche, ` quella di prendere in considerae zione altri linguaggi di programmazione. In ogni caso, in questa tesi la grammatica ` stata sviluppata in modo e tale da isolare ogni singola parte degli statement previsti; ` ragionee 37
  • 44. 6. Conclusioni 38 vole pensare che considerando invece statement interi la probabilit` di a giungere a mutazioni fallimentari diminuisca.
  • 45. Capitolo 7 Ringraziamenti Desidero ringraziare in maniera sentita il prof. Alberto Bartoli per avermi offerto la possibilit` di far parte del suo laboratorio, a concedendomi, cos` di trascorrere un periodo ı, a stretto contatto con il mondo della ricerca. Un grazie al prof. Medvet, o meglio Eric, che si ` dimostrato spesso polemico ma sempre costruttivo. e Un grazie al dott. De Lorenzo, per noialtri Andrea, sempre disponibile a chiarire tutti i miei dubbi e senza il quale questo lavoro non sarebbe stato n´ meno difficile e n´ pi` divertente :P e u Impossibile dimenticare Giulio e Daniele gli altri tesisti che hanno lavorato con me nel mai banale Machine Learning Lab. Un ringraziamento che non pu` trovare lo spazio appropriato o fra le ultime righe di questa tesi, lo devo ai miei amici, vecchi e nuovi, ed ai miei fantastici coinquilini, che hanno allietato il mio passato, che rendono unico il mio presente, e che spero continueranno a farmi dono della loro presenza nel mio futuro. Non mi sembra opportuno ringraziarli ad uno ad uno, perch´ sarebbe un elenco troppo lungo e e soprattutto perch´ citare i loro nomi e 39
  • 46. Ringraziamenti 40 conferirebbe un ordine implicito che non renderebbe giustizia e non rispecchierebbe i miei sentimenti. Un immenso grazie va ad Alessio, per avermi sostenuto in questo percorso universitario, per essersi sub` i miei ıto sfoghi pi` o meno fondati ma, soprattutto, u per avermi sopportato tutti questi anni e, spero, continuer` a farlo per molti anni a venire. a Infine, il mio pensiero va ai miei genitori i quali mi hanno dato tutto un affetto senza limiti, una giusta educazione, un sostegno nei periodi di necessit`. a . . . ai miei genitori che hanno favorito le mie passioni e che tuttora si prodigano in questa direzione. . . . ai miei genitori perch´ credo e non avrei potuto averne di migliori. Come ultimissima cosa vorrei invece invitare chi mi ha ripetutamente smontato e criticato a farsi un esame di coscienza ed ammettere che, in fondo, qualche torto ce l’avesse. Auguro a costoro di guarire dal brutto morbo dell’invidia.