_ _⇒(AbortCode opstack_error state) end
end end end ) end end end end.
3.5 Gestion des erreurs et exceptions
Lors de l’ex´ecution d’un programme, le d´eroulement normal des calculs peut ˆetre interrompu par un programme `a la forme incorrecte ou une condi-tion non r´ealis´ee d’un calcul. Dans le premier cas, il s’agit d’une erreur du programme (comme l’appel `a l’instructionpop avec une pile vide, un typage incorrect, ...) qui conduit `a la terminaison abrupte de l’ex´ecution. Dans le second cas, le m´ecanisme des exceptions, pr´esent dans beaucoup de langages de programmation r´ecents et qui se traduit par un changement dans le flot de contrˆole normal du programme, permet un gestion efficace de ces erreurs dynamiques d’ex´ecution.
3.5.1 Les erreurs
A nouveau, l’origine des erreurs dans la machine virtuelle peut provenir` de diff´erentes sources. Il peut s’agir :
– d’une erreur du code (branchement `a une adresse incorrecte, pile vide, ...) ;
– d’une incoh´erence du programme (classe inexistante, champ man-quant, ...) ;
– d’une incoh´erence de la m´emoire (objet non trouv´e ou pas du type attendu).
Le signalement de ces erreurs dans la machine virtuelle s’effectue respecti-vement avec l’utilisation des fonctionsAbortCode,AbortCapetAbortMemory. Ces fonctions construisent un ´etat anormal (Abnormal) `a partir de la raison et de l’´etat donn´es en argument. La raison, du type inductifeReason
suivant, n’est pr´esente que pour aider `a rendre plus claire la cause de l’erreur :
Inductive eReason : Set :=
heap_error : eReason | sheap_error : eReason | opstack_error : eReason | checkcast_error : eReason | store_error : eReason | overflow_error : eReason | init_error : eReason | [...]
L’ex´ecution ne peut continuer `a partir d’un ´etat anormal et la machine virtuelle termine.
3.5.2 Les exceptions
EnJava, lors du lancement d’une exception, un objetJava est cr´e´e dont le type donne la raison de l’erreur. Un m´ecanisme de rattrapage de l’excep-tion intervient alors et recherche parmi la pile des contextes d’ex´ecution de m´ethode la m´ethode contenant le code `a ex´ecuter lorsqu’une telle exception est lanc´ee. Le flot de contrˆole est alors transmis au d´ebut de ce code et l’ex´ecution continue.
Le lancement d’exceptions peut ˆetre r´ealis´e ou bien directement par la machine virtuelle (dans le cas d’une division par z´ero par exemple) ou par le programmeur (utilisation du mot-cl´eJavathrowdans le code source, traduit vers le code octetathrow).
Lorsque la machine virtuelle lance d’elle-mˆeme une exception, l’´etat de retour de l’instruction ex´ecut´ee a pour constructeur ThrowException et
comme arguments l’´etat courant et un terme du typexLabelindiquant l’ex-ception `a lancer :
Inductive xLabel : Set :=
Arithmetic : xLabel |
ArrayStore : xLabel |
ClassCast : xLabel |
IndexOutOfBounds : xLabel | ArrayIndexOutOfBounds : xLabel | NegativeArraySize : xLabel | NullPointer : xLabel |
Security : xLabel .
Un objet java du type correspondant `a l’exception voulue est alors cr´e´e dans le tas et la fonction CatchException (dont le comportement est d´ecrit `a la section 3.5.3, qui suit) est appel´ee.
Dans le cas du code octet athrow, l’objet repr´esentant l’exception `a lancer a d´ej`a ´et´e cr´e´e par des codes octet pr´ec´edant l’instruction et la fonc-tionCatchException est, apr`es quelques v´erifications sur cet objet, appel´ee
´egalement.
3.5.3 Récupération d’exceptions
La r´ecup´eration des exceptions est principalement un probl`eme de re-cherche dans la pile des contextes d’ex´ecution.
Chaque m´ethode poss`ede sa liste de gestionnaires d’exceptions, il s’agit du champ handler_list de l’enregistrement Method (cf section 3.2.2). Un gestionnaire d’exception est d´ecrit par le typehandler_type:
Definition handler_type :=
(bytecode_idx*bytecode_idx*cap_class_idx*bytecode_idx).
o`u les deux premiers index de code octet indiquent la port´ee du code dans la m´ethode (un bloc) o`u doit se trouver le pointeur d’ex´ecution pour qu’une exception de type compatible avec la classe donn´ee en troisi`eme composante puisse ˆetre g´er´ee. La derni`ere composante indique l’emplacement du code (`a l’int´erieur du code de la m´ethode) o`u brancher dans le cas o`u l’exception est r´ecup´er´ee par ce gestionnaire.
Une m´ethode est alors en mesure de rattraper une exception si elle pos-s`ede un gestionnaire d’exception tel que :
– sa port´ee inclut le pointeur d’ex´ecution ;
– l’exception lev´ee est une instance ou une instance d’une sous-classe de la classe g´er´ee.
Lors du lancement d’une exception, le m´ecanisme de r´ecup´eration d’ex-ception commence par observer si la m´ethode courante (celle du haut de la pile de contextes d’ex´ecution) peut g´erer l’exception. Le cas ´ech´eant, l’ex´ e-cution reprend `a l’emplacement indiqu´e par le gestionnaire d’exception. Si-non, le contexte d’ex´ecution courant est enlev´e de la pile des contextes et l’algorithme recommence. Cela se traduit simplement par la fonction Coq suivante (une des seules de la formalisation `a ˆetre exprim´ee par un point fixe) :
Fixpoint lookup_stack [s:stack] :
Class→jcprogram→(option (bytecode_idx*stack)) :=
[cl:Class][cap:jcprogram]
Cases s of
(∗ s t a c k empty : e x c e p t i o n uncaught ∗) nil ⇒(None ?) |
(cons h lf)⇒
(∗ l o o k u p i n c u r r e n t frame ∗) Cases (lookup_frame cl h cap) of
(∗ h a n d l e r found i n h : r e t u r n b r a n c h i n g p o i n t and c u r r e n t s t a c k ∗) (Some u)⇒ (Some ? (u,s)) |
(∗ no h a n d l e r found i n frame h : c o n t i n u e l o o k u p i n l f ∗) None ⇒(lookup_stack lf cl cap)
end end.
On remarque qu’il s’agit d’une fonction partielle et que l’on peut se trouver dans une situation o`u aucun contexte ne g`ere l’exception. L’ex´ecution du programme s’arrˆete alors.
On notera ´egalement qu’`a l’int´erieur d’un mˆeme contexte d’ex´ecution, plusieurs gestionnaires peuvent g´erer une exception. Le gestionnaire choisi est alors celui dont la port´ee du bloc gestionnaire est la moins large.