1 / 50

Semantica Operazionale di un frammento di Java: le regole di transizione

Semantica Operazionale di un frammento di Java: le regole di transizione. estensione (con piccole varianti) di quella in Barbuti, Mancarella, Turini, Elementi di Semantica Operazionale, appunti di Fondamenti di Programmazione. Regole di transizione.

abiba
Télécharger la présentation

Semantica Operazionale di un frammento di Java: le regole di transizione

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Semantica Operazionale di un frammento di Java: le regole di transizione estensione (con piccole varianti) di quella in Barbuti, Mancarella, Turini, Elementi di Semantica Operazionale, appunti di Fondamenti di Programmazione

  2. Regole di transizione • regole che ci dicono come viene modificato uno stato per effetto dell’esecuzione di un certo costrutto del linguaggio • diverse relazioni di transizione, in corrispondenza dei diversi tipi di costrutti del linguaggio • espressioni, comandi, dichiarazioni di variabile, dichiarazioni di metodi, dichiarazioni di classe, ecc.

  3. Dichiarazione di classe Class_decl := class Id extends Id { Static_var_decl_list Static_meth_decl_list Inst_var_decl_list Inst_meth_decl_list Costruttore } • assunzioni • c’è sempre extends • ogni classe estende la classe Object • le dichiarazioni di variabili e metodi statici sono “raggruppate” • esiste sempre il costruttore

  4. Semantica informale delle classi • variabili e metodi statici appartengono alla classe • variabili e metodi di istanza appartengono agli oggetti (istanze della classe) • una classe definisce un tipo (nome della classe) • gli oggetti istanza hanno quel tipo • relazioni di sottoclasse ed ereditarietà • gerarchia di tipi, realizzata con extends • la radice della gerarchia è la classe predefinitaObject

  5. Ereditarietà • se c1 è una sottoclasse (estende) c2 • variabili e metodi statici dic2(e delle sue superclassi) sono visibili direttamente dac1 • variabili e metodi di istanza di c2 (e delle sue superclassi) diventano anche variabili e metodi di istanza di c1 • a meno di overriding

  6. Overriding • se c1 è una sottoclasse (estende) c2 • un metodo di istanza dic2(e delle sue superclassi) può essere ridefinito (stesso nome e tipi) inc1 • l’idea è quella di definire un metodo più specializzato, che rimpiazza quello definito in una delle superclassi • l’overriding non “dovrebbe” essere possibile per le variabili e per i metodi statici • ma questo non è sempre garantito dal compilatore • comportamenti complessi e poco prevedibili • assumeremo che non si verifichi mai

  7. Inizializzazioni • Java permette di inizializzare (al valore di una espressione) sia le variabili statiche che quelle di istanza in una dichiarazione • le inizializzazioni delle variabili statiche vengono effettuate la prima volta che si usa una classe • le inizializzazioni delle variabili di istanza vengono effettuate all’atto della creazione di una istanza • in tutti e due i casi, l’inizializzazione può riguardare anche le superclassi • per semplicità, decidiamo di non avere inizializzazioni nelle dichiarazioni di variabile all’interno di classi • per questo ci aspettiamo che esista sempre un costruttore • eseguito quando si crea un’istanza

  8. Il costruttore • i costruttori sono usati per la vera inizializzazione delle variabili di istanza • anche per i costruttori esiste un meccanismo di ereditarietà • se c è una classe che ha come superclassi (nell’ordine) le classi c1, c2, …, cn=Object, • all’atto della creazione di una istanza di c • si eseguono (nell’ordine) i costruttori di cn, …, c2, c1, c

  9. Dichiarazione di classe Class_decl := class Id extends Id { Static_var_decl_list Static_meth_decl_list Inst_var_decl_list Inst_meth_decl_list Costruttore } Cenv = Id -> Cdescr Cdescr = Id * Frame * Menv * Frame* Menv • la relazione di transizione Class_decl * Cenv cdecl Cenv

  10. Dichiarazione di classe Cenv = Id -> Cdescr Cdescr = Id * Frame * Menv * Frame* Menv Class_decl * CenvcdeclCenv r(b) = (_,_,_, j, m) m(b) = (_, c1, _) <svdl, newframe()> vdecl j1 <ivdl, copy(j)> vdecl j2 <smdl, a, memptyenv()> mdecl m1 <imdl, a, m> mdecl m2 ________________________________________________ <class a extends b {svdl smdl ivdl imdl c}, r> cdecl cbind(r, a, (b, j1, m1 , j2, mbind(m2, a, ([], c1 c, a))))

  11. Commenti r(b) = (_,_,_, j, m) m(b) = (_,c1,_) <svdl, newframe()> vdecl j1 <ivdl, copy(j)> vdecl j2 <smdl, a, memptyenv()> mdecl m1 <imdl, a, m> mdecl m2 ________________________________________________ <class a extends b {svdl smdl ivdl imdl c}, r> cdecl cbind(r, a, (b, j1, m1 , j2, mbind(m2, a, ([], c1 c, a)))) • il frame delle variabili di istanza viene costruito a partire da una copia di quello della superclasse • i frames sono strutture modificabili • è necessario costruire la copia • l’ambiente dei metodi di istanza viene costruito a partire da quello della superclasse • gli ambienti di metodi sono funzioni • l’eventuale overriding è realizzato automaticamente da mbind • il costruttore è ottenuto componendo (sintatticamente) il corpo del costruttore della superclasse con quello “nuovo”

  12. Dichiarazioni di metodi Method_decl := Id (Idlist) Blocco • guardiamo solo la dichiarazione singola • per una lista di dichiarazioni, si ripete a partire dal risultato precedente Menv = Id -> Mdescr Mdescr = Idlist * Blocco * ( Loc | Id ) • la relazione di transizione Method_decl * Id * Menv mdeclMenv

  13. Dichiarazione di metodo Menv = Id -> Mdescr Mdescr = Idlist * Blocco * ( Loc | Id ) Method_decl * Id * MenvmdeclMenv <m (idlist) blocco, c, m> mdecl mbind(m, m, (idlist, blocco, c)) • come già osservato, l’eventuale overriding è realizzato automaticamente dambind

  14. Dichiarazioni di variabili Var_decl := Type Id; • come già osservato, le dichiarazioni non hanno inizializzazione • guardiamo solo la dichiarazione singola • per una lista di dichiarazioni, si ripete a partire dal risultato precedente Frame = Id -> Val Val = (Bool | Int | Loc) • la relazione di transizione Var_decl * Frame vdeclFrame

  15. Dichiarazione di variabile Frame = Id -> Val Val = (Bool | Int | Loc) Var_decl * Frame vdeclFrame <t id;, j> vdeclbind(j, id, default(t)) • come già osservato, non ci deve essere overriding • idnon deve essere definito inj • in realtà un Frame è una struttura modificabile • bind modifica l’argomento • la funzione default genera un valore di tipo t • 0 se t è intero • null se t è il tipo di un oggetto

  16. Espressioni • dato che non abbiamo previsto inizializzazioni per le variabili (statiche e di istanza) all’interno delle classi, le espressioni possono comparire solo all’interno dei metodi • consideriamo solo alcuni dei casi di espressioni • quelli che riguardano oggetti

  17. Espressioni Expr := new Id | (creazione di istanza) Path Id (accesso al valore di una variabile) Path := Id. Objpath | (si parte dalla classe Id) this. Objpath | (si parte dall’oggetto corrente) Objpath Objpath := | (cammino vuoto) Id. Objpath (si parte dall’oggetto Id) • ambigua, ma....

  18. La relazione di transizione per le espressioni Expr := new Id | (creazione di istanza) Path Id (accesso al valore di una variabile) Expr * Cenv * Heap * Astack expr Val * Heap

  19. Creazione di oggetti Expr * Cenv * Heap * AstackexprVal * Heap r(a) = (_,_,_, j, m) l = newloc(z) m’ = instantiate(m,l) j’ = copy(j) z’’ = hbind(z, l, (a, j’, m’)) m’(a) = (_, b, _) s’= push(s, (l, push(emptystack(), newframe()))) <b, r, z’’, s’>com <z’’’, s’’,r’> pop(s’’) ________________________________________________ <new a, r, z, s> expr<l, z’’’ >

  20. Creazione di oggetti: commenti r(a) = (_,_,_, j, m) l = newloc(z) m’ = instantiate(m,l) j’ = copy(j) z’’ = hbind(z, l, (a, j’, m’)) m’(a) = (_, b, _) s’= push(s, (l, push(emptystack(), newframe()))) <b, r, z’’, s’>com <z’’’, s’’,r’> pop(s’’) ________________________________________________ <new a, r, z, s> expr<l, z’’’ > • l’oggetto contiene • il nome della classe • una copia del frame delle variabili di istanza • una specializzazione sull’oggetto dell’ambiente di metodi di istanza • il costruttore viene eseguito come una invocazione di metodo • non facciamo vedere i possibili effetti “nascosti” sui frames di variabili statiche (modifica dirda parte dicom )

  21. Risoluzione di nomi (semplici) • il riferimento ad un identificatore (non qualificato) può in generale essere risolto • nella pila di frames sulla testa dello stack delle attivazioni • nei frames delle variabili di istanza di un oggetto • nei frames di variabili statiche di una classe • il modo di effettuare la ricerca dipende da dove essa inizia • un metodo (quello correntemente attivo) • un oggetto • una classe Ide * (Ide | Loc | met) * Cenv * Heap * Astack naming Val

  22. Risoluzione di nomi 1 Ide * (Ide | Loc | met) * Cenv * Heap * Astack naming Val r(c) = (c1, j, _, _, _) defined(j, i) _____________________________________________ <i, c:Ide, r, z , s > naming j(i) r(c) = (c1, j, _, _, _) not defined(j, i) <i, c1, r, z , s > naming v _____________________________________________ <i, c:Ide, r, z , s > naming v

  23. Risoluzione di nomi 2 Ide * (Ide | Loc | met) * Cenv * Heap * Astack naming Val z(l) = (c, j, _) defined(j, i) _____________________________________________ <i, l:Loc, r, z , s > naming j(i) z(l) = (c, j, _) not defined(j, i) <i, c, r, z , s > naming v _____________________________________________ <i, l:Loc, r, z , s > naming v

  24. Risoluzione di nomi 3 Ide * (Ide | Loc | met) * Cenv * Heap * Astack naming Val top(s) = (x, t) j = top(t) defined(j, i) _____________________________________________ <i, met, r, z , s > naming j(i) top(s) = (x, t) j = top(t) not defined(j, i) s’ = push(pop(s), (x, pop(t)))<i, met, r, z , s’> naming v _____________________________________________ <i, met, r, z , s > naming v top(s) = (x, t) empty(t) <i, x, r, z , s > naming v _____________________________________________ <i, met, r, z , s > naming v

  25. Qualificazione dei nomi (paths) Path := Id. Objpath | (si parte dalla classe Id) this. Objpath | (si parte dall’oggetto corrente) Objpath Objpath := | (cammino vuoto) Id. Objpath (si parte dall’oggetto Id) • la semantica di un path determina il punto di partenza di una operazione di naming Path * (Ide | Loc | met) * Cenv * Heap * Astack path (Ide | Loc | met)

  26. Qualificazione dei nomi 1 Path * (Ide | Loc | met) * Cenv * Heap * Astack path (Ide | Loc | met) < , x, r, z , s > path x top(s) = (o, _) ____________________________ <this, met, r, z , s > path o cdefined(r, c) ____________________________ <c, met, r, z , s > path c

  27. Qualificazione dei nomi 2 Path * (Ide | Loc | met) * Cenv * Heap * Astack path (Ide | Loc | met) not cdefined(r, o) <o, x, r, z , s > naming o’ __________________________________________ <o, x, r, z , s > path o’ <p, x, r, z , s > path p’ <pl, p’, r, z , s > path o _________________________________________________ <p.pl, x, r, z , s > path o

  28. Come trovare il valore di un nome qualificato Expr := Path Id Expr * Cenv * Heap * Astack expr Val * Heap <p, met, r, z , s > path o <a, o, r, z , s > naming v ___________________________________________________ <p a, r, z , s > expr<v, z > • i controlli statici garantiscono che il nome (nel frame opportuno) abbia già ricevuto un valore (inizializzazione o assegnamento) prima dell’uso

  29. I comandi • un blocco (corpo di un metodo) è una sequenza di dichiarazioni e comandi (che possono anche essere blocchi nidificati) • la composizione sequenziale viene trattata in modo standard • nel costrutto c;cl valutiamo cl nello stato risultante dalla valutazione di c • trattiamo anche le dichiarazioni di variabili (locali al metodo) come comandi • vediamo solo alcuni casi di comandi • condizionali e loops hanno la semantica vista nel primo corso Com * Cenv * Heap * Astack com Heap * Astack * Cenv

  30. Sintassi dei comandi Com := {Com} | (blocco annidato) Path Ide = Expr | (assegnamento) Type Ide = Expr | (dichiarazione) Path Id (Expr_list) | (invocazione di metodo) Com * Cenv * Heap * Astack com Heap * Astack * Cenv

  31. Semantica dei blocchi Com := {Com} (blocco annidato) Com * Cenv * Heap * Astack com Heap * Astack * Cenv top(s) = (x, t) s’=push(pop(s), (x, push(t , newframe()))) <c, r, z, s’> com <z’, s’’,r’ > top(s’’) = (x, t’) ________________________________________________ <{c}, r, z, s> com <z’, push(pop(s’’), (x, pop(t’))),r’ >

  32. Semantica delle dichiarazioni Com := Type Ide = Expr (dichiarazione) Com * Cenv * Heap * Astack com Heap * Astack * Cenv top(s) = (x, t) j = top(t ) <e, r, z, s> expr <v, z’ > j’= bind(j , i, v) s’=push(pop(s), (x,push(pop(t ) , j’))) _________________________________________ <t i = e, r, z, s> com <z’ , s’,r > • la bind “modifica”jdentros

  33. Semantica dell’assegnamento 1 Com := Path Ide = Expr (assegnamento) Com * Cenv * Heap * Astack com Heap * Astack * Cenv • l’identificatore per cui va modificata l’associazione in un frame (a parte la qualificazione) può in generale essere • nella pila di frames sulla testa dello stack delle attivazioni • nei frames delle variabili di istanza di un oggetto • nei frames di variabili statiche di una classe • il modo di effettuare la ricerca dipende da dove essa inizia • un metodo (quello correntemente attivo) • un oggetto • una classe • simile al naming Ide * (Ide | Loc | met) * Cenv * Heap * Astack update Heap * Astack * Cenv

  34. Semantica dell’assegnamento 2 Com := Path Ide = Expr (assegnamento) Com * Cenv * Heap * Astack com Heap * Astack * Cenv <p, met, r, z , s > path c <e, r, z, s> expr <v, z’’ > <i, v, c, r, z’’, s> update<z’, s’ , r’ > _____________________________ <p i = e, r, z, s> com <z’, s’ , r’ > Ide * Val * (Ide | Loc | met) * Cenv * Heap * Astack update Heap * Astack * Cenv

  35. Update 1 Ide * Val * (Ide | Loc | met) * Cenv * Heap * Astack update Heap * Astack * Cenv r(c) = (c1, j1, m1 , j2, m2) defined(j1, i) j’= update (j1, i, v) r’= cbind (r, c, (c1, j’, m1 , j2, m2) ) _____________________________________________ <i, v, c:Ide, r, z , s > update <z , s, r’> r(c) = (c1, j, _, _, _) not defined(j, i) <i, v, c1, r, z , s > update <z’, s’, r’> _____________________________________________ <i, v, c:Ide, r, z , s > update <z’, s’, r’>

  36. Update 2 Ide * Val * (Ide | Loc | met) * Cenv * Heap * Astack update Heap * Astack * Cenv z(l) = (c, j, m) defined(j, i) z’ = hbind(z , l, (c, update(j, i, v), m)) _____________________________________________________________ <i, v, l:Loc, r, z , s > update < z’, s, r > z(l) = (c, j, _) not defined(j, i) <i, v, c, r, z , s > update <z’, s’, r’> _____________________________________________ <i, v, l:Loc, r, z , s > update <z’, s’, r’>

  37. Update 3 Ide * Val * (Ide | Loc | met) * Cenv * Heap * Astack update Heap * Astack * Cenv top(s) = (x, t) j = top(t) defined(j, i) s’ = push(pop(s), (x, push(pop(t) , update (j, i, v)))) __________________________________________________________ <i, v, met, r, z , s > update <z , s’, r > top(s) = (x, t) j = top(t) not defined(j, i) s’ = push(pop(s), (x, pop(t))) <i, v, met, r, z , s’ > update <z’’, s’’, r’> top(s’’) = (x, t’) s’’’ = push(pop(s’’), (x, push(t’, j))) _________________________________________________________________ <i, v, met, r, z, s > update <z’’, s’’’, r’> top(s) = (x, t) empty(t) <i, v, x, r, z , s > update <z’’, s’’, r’> _________________________________________________________________ <i, v, met, r, z , s > update <z’’, s’’, r’>

  38. Invocazione di metodi Com := Path Id (Expr_list) • path vuoto • paths trattati come nelle variabili • un solo parametro • vediamo, per prima cosa, come si ritrova un metodo di nome f • a partire da un oggetto o da una classe Ide * (Ide | Loc) * Cenv * Heapfmet Ide * Blocco * (Ide | Loc)

  39. Ricerca dei metodi 1 Ide * (Ide | Loc) * Cenv * Heapfmet Ide * Blocco * (Ide | Loc) r(c) = (c1, _, m, _, _) mdefined(f,m) _____________________________________________ <f, c:Ide, r, z> fmet m(f) r(c) = (c1, _, m, _, _) not mdefined(f,m) <f, c1, r, z> fmet md _____________________________________________ <f, c:Ide, r, z> fmet md

  40. Ricerca dei metodi 2 Ide * (Ide | Loc) * Cenv * Heapfmet Ide * Blocco * (Ide | Loc) z(l) = (c, _, m) mdefined(f,m) _____________________________________________ <f, l:Loc, r, z> fmet m(f) z(l) = (c, _, m) not mdefined(f,m) <f, c, r, z> fmet md _____________________________________________ <f, c:Ide, r, z> fmet md

  41. Invocazione di metodi Com := Id (Expr) Com * Cenv * Heap * AstackcomHeap * Astack * Cenv Ide * (Ide | Loc) * Cenv * Heapfmet Ide * Blocco * (Ide | Loc) top(s) = (x, _) <f, x, r, z> fmet (par, b,y) <e, r, z, s> expr <v, z’ > j = bind(newframe(), par, v) s’ = push(s, (y, push(emptystack(), j))) <b, r, z’, s’>com <z’’,s’’, r’ > ________________________________________________ <f(e), r, z, s)> com <z’’,pop(s’’), r’ >

  42. Invocazione di metodi: commenti top(s) = (x, _) <f, x, r, z> fmet (par, b,y) <e, r, z, s> expr <v, z’ > j = bind(newframe(), par, v) s’ = push(s, (y, push(emptystack(), j))) <b, r, z’, s’>com <z’’,s’’, r’ > ________________________________________________ <f(e), r, z, s)> com <z’’,pop(s’’), r’ > • si effettua la ricerca del metodo a partire dalla classe o dall’oggetto contenuti nella testa della pila • si valuta il parametro attuale • si crea un nuovo stack di frames, il cui unico frame contiene l’associazione tra parametro formale e valore del parametro attuale • si pusha sulla pila il record che contiene la classe o l’oggetto associato al metodo e la pila di frames • si valuta il corpo del metodo

  43. Il naming • naming • tutti gli usi di nomi all’interno dei metodi (inclusi quelli usati nei paths) • variabili locali, variabili di istanza, variabili statiche, metodi di istanza, metodi statici sono staticamente controllati per verificarne l’esistenza in accordo con le regole di visibilità • quelle che abbiamo “implementato” nei vari meccanismi di naming • le regole di visibilità tengono anche conto degli attributi private, public, protected • il meccanismo dei packages (con esportazioni ed importazioni) • serve per raggruppare insiemi di classi introduce ulteriori restrizioni

  44. I tipi • ogni entità (variabile, metodo) ha un tipo determinato staticamente • type checking statico • nell’assegnamento e nel passaggio di parametri il tipo del valore è garantito solo essere un sottotipo di quello della variabile • per le variabili che denotano oggetti, ci può essere una differenza tra • il tipo apparente = quello della dichiarazione della variabile o del parametro formale • il tipo effettivo a run-time = quello costruito valutando l’espressione in un assegnamento o il parametro attuale • il type checking statico può solo tener conto del tipo apparente • la differenza tra tipo apparente e tipo effettivo per gli oggetti si riflette anche sulla verifica dei nomi, che devono essere visibili secondo il tipo apparente

  45. Altre osservazioni generali • l’esecuzione inizia con l’invocazione del metodo (statico) main presente nell’ultima dichiarazione di classe • astrazioni sui dati utili (come le stringhe e gli arrays) sono definite da classi “primitive” • modificabilità delle strutture dati e condivisione (sharing) • gli oggetti di una classe possono essere • modificabili, se hanno delle operazioni che li modificano (per esempio, gli arrays) • non modificabili (per esempio, le stringhe) • un oggetto è condiviso da due entità se può essere raggiunto da entrambi • se è modificabile, le modifiche si riflettono su tutte le entità che lo “contengono” • effetti laterali (all’esterno) in un metodo • attraverso i parametri può solo modificare valori di tipo oggetto • può modificare variabili di istanza e variabili (statiche) di classe che vede

  46. Il garbage collector • la gestione dinamica della heap è garantita dalla presenza di un garbage collector nella JVM • quando la heap è piena e non sarebbe più possibile creare dinamicamente nuovi oggetti, il garbage collector riconosce tutti gli oggetti che non sono più raggiungibili a partire dalla pila e dall’ambiente delle classi e li “raccoglie”, rendendo disponibile la memoria relativa • è forse la principale causa del successo di Java rispetto ad altri linguaggi orientati ad oggetti (C++) • dove la restituzione degli oggetti “inutili” è a carico del programmatore, con gravi rischi nel caso di oggetti condivisi • prima di Java, il garbage collector era presente quasi esclusivamente in linguaggi funzionali (LISP, ML) e logici (PROLOG) • in cui le strutture allocate sulla heap sono molto più semplici e regolari

  47. Java e affidabilità • la affidabilità • intesa come protezione da errori a tempo di esecuzione è garantita al meglio in Java da vari meccanismi • il controllo dei nomi statico • a run time non è possibile tentare di accedere un nome non definito • il controllo dei tipi statico • a run time non si possono verificare errori di tipo (ma...) • la presenza del garbage collector • non è possibile tentare di accedere oggetti non più presenti nella heap • la presenza di controlli a tempo di esecuzione, con sollevamento di eccezioni • controllo sugli indici per gli arrays • accesso a puntatori ad oggetti vuoti (null) • conversioni forzate di tipo (casting)

  48. Risoluzione dei nomi nella compilazione • il compilatore Java • anche per fare il controllo dei nomi costruisce staticamente delle tabelle che corrispondono ai frames (della pila, delle variabili di istanza, delle variabili statiche), agli ambienti dei metodi (statici e di istanza) ed all’ambiente delle classi • la posizione di un nome (di variabile, di metodo o di classe) all’interno di queste tabelle è decisa una volta per tutte a tempo di compilazione • i nomi possono tutti scomparire dalle tabelle, che diventano vettori di valori, descrizioni di classe o descrizioni di metodi • i riferimenti ai nomi (tipicamente nel codice dei metodi) possono essere rimpiazzati da “displacements” (posizioni) in una opportuna tabella • una ulteriore semplificazione è possibile per i metodi, dato che, a differenza dei valori delle variabili nei frames, essi non possono essere modificati • il riferimento al nome può essere direttamente rimpiazzato dal puntatore al codice e la tabella può essere eliminata • questo non è possibile per i metodi di istanza, a causa della possibile differenza tra tipo apparente e tipo effettivo (dispatching)

  49. (y,z) C f1 f2 (w) C () f3 ? (x) ? C () ? f4 Come diventa lo stato (1) puntatore ad oggetto codice blocco f1 • parte statica (classi) codice blocco f2 codice blocco f3 codice blocco f4 a 23 d ? A A C b e ? codice blocco C c 5 2 B A Object

  50. () f3 d 3 (x) e C () Come diventa lo stato (2) • parte dinamica (pila e heap) • è in esecuzione il metodo f4 di una istanza di C C f4 x 4 stack heap puntatori al codice dei metodi

More Related