Les erreurs
Attraper, lever et nettoyer avec TRY, CATCH et FINALLY, choisir ce que fait une commande qui échoue avec ErrorMode, et lire les erreurs que signale l'éditeur.
En un mot#
Un programme rencontre deux sortes d'erreurs. Le compilateur trouve la première
avant que rien ne tourne. La seconde survient pendant que le jeu tourne, et
l'arrête sauf si vous l'attrapez avec TRY … CATCH. Les commandes du
catalogue ont une option de plus : ErrorMode décide si leurs échecs arrêtent,
signalent ou restent muets.
Deux sortes d'erreurs#
| Erreur de compilation | Erreur d'exécution | |
|---|---|---|
| Quand | avant que le programme démarre | pendant qu'il tourne |
| Exemples | une commande mal orthographiée, ENDIF sans IF, RETHROW hors d'un CATCH | une commande qui échoue, THROW, a DIV 0, une poignée libérée |
| Ce qui se passe | chaque erreur est listée, et rien ne tourne | le programme s'arrête à cette ligne, sauf si un TRY l'attrape |
| Peut-on l'attraper | non, corrigez la source | oui, avec TRY / CATCH |
Ce qu'affiche la console#
Les deux sortes vont dans la console de l'éditeur, sur trois lignes au plus :
BASIC · Compilation · <fichier> · Ligne <n>, colonne 1
SyntaxError: <message>
BASIC · Exécution · <fichier> · Ligne <n>, colonne <c>
<nom>: <message>
Commande : <l'appel qui a échoué>
- Le fichier est le script, ou le module où se trouve la ligne quand le
programme est découpé avec
#INCLUDE. Voir Le préprocesseur. - La ligne
Commande :apparaît quand une commande du catalogue a échoué. Elle montre l'appel avec les valeurs reçues, par exempleMemblockByte(999, 0). - Double-cliquez une ligne d'erreur pour ouvrir le script à cette ligne.
L'échec d'une commande ressemble à ceci :
RangeError: BASIC MemblockByte(999, 0) failed: Classic BASIC memory block 999 does not exist. Create or load the referenced resource before running this command.
Commande : MemblockByte(999, 0)
⚠️ Un message JavaScript en phase Compilation signale une faille du compilateur. « Unexpected identifier », « missing ) after argument list », « await is only valid in async functions » ou « Identifier … has already been declared » viennent d'une forme que le compilateur a acceptée alors qu'il aurait dû la refuser. Les chapitres signalent ces formes d'un ⚠️ et donnent la graphie qui marche.
TRY, CATCH, FINALLY#
TRY
<statements>
[CATCH [<name>[$]]
<statements>]
[FINALLY
<statements>]
ENDTRY
TRY
THROW "plus de munitions"
CATCH problem$
Console("attrapé : " + problem$)
FINALLY
Console("nettoyage")
ENDTRY
Console("après : " + problem$)
Le programme affiche attrapé : plus de munitions, puis nettoyage, puis
après : plus de munitions.
CATCHne s'exécute que si quelque chose a échoué dans la partieTRY.FINALLYs'exécute toujours : après une fin normale, après une erreur, aprèsEXITFUNCTIONouRETURN, aprèsENDet après unGOTOqui sort du bloc.CATCHetFINALLYsont chacun optionnels, mais unTRYen demande au moins un : « TRY requires CATCH or FINALLY ».- L'ordre est fixe :
TRY, puisCATCH, puisFINALLY. Un seulCATCHau plus : il n'y a pas deCATCHpar type d'erreur. Dans leCATCH, testez plutôt lenameou lecodede l'erreur. ENDTRYetEND TRYsont la même chose.- Un
TRYmarche partout : dans le programme principal, dans la boucle d'images, dans uneFUNCTION, dans une fonction du cycle de vie commeupdate.
Les erreurs de compilation de ce bloc :
| Message | Cause |
|---|---|
CATCH without TRY, FINALLY without TRY, ENDTRY without TRY | le TRY d'ouverture manque |
Duplicate CATCH in TRY, Duplicate FINALLY in TRY | une seconde clause du même genre |
CATCH cannot follow FINALLY | mauvais ordre |
TRY requires ENDTRY | le bloc n'est jamais fermé |
La variable du CATCH#
Le nom placé après CATCH reçoit l'erreur. Son sigil décide de ce que vous
obtenez.
| Vous écrivez | La variable contient |
|---|---|
CATCH problem$ | le message, sous forme de chaîne |
CATCH problem | un enregistrement en lecture seule, à quatre champs |
CATCH | rien : l'erreur est attrapée puis oubliée |
| Champ | Contenu |
|---|---|
problem.name | la sorte d'erreur : Error pour un THROW, RangeError, TypeError… pour les autres |
problem.message | le message |
problem.code | le code donné à THROW, ou "" |
problem.command | l'appel de commande qui a échoué, comme MemblockByte(999, 0), ou "" |
TRY
value = MemblockByte(999, 0)
CATCH failure
Console(failure.name) ' RangeError
Console(failure.command) ' MemblockByte(999, 0)
Console(failure.message) ' BASIC MemblockByte(999, 0) failed: Classic BASIC memory block 999 does not exist. …
ENDTRY
La variable reste lisible après ENDTRY, comme le montre le premier
exemple. Dans une FUNCTION, elle appartient à la fonction.
⚠️ Donnez à la variable du CATCH un nom bien à elle. Si le même nom est
déclaré ou affecté ailleurs dans la même portée, avant ou après le TRY, le
compilateur l'accepte mais le programme ne peut pas démarrer (« Identifier … has
already been declared »). Pour garder le message, copiez-le dans une autre
variable : report$ = "dernière erreur : " + problem$.
OPTION EXPLICIT
GLOBAL message$ = ""
TRY
THROW "x"
CATCH message$
ENDTRYTHROW#
THROW <value>
THROW lève une erreur qu'un CATCH peut recevoir. Ce que vous levez décide
des champs :
| Vous levez | message | code |
|---|---|---|
une chaîne : THROW "échec de la sauvegarde" | la chaîne | "" |
un nombre : THROW 404 | le nombre en texte, 404 | "" |
un enregistrement avec un champ message, et éventuellement un champ code | le champ message | le champ code |
TYPE Failure
message AS string
code AS integer
ENDTYPE
f AS Failure
f.message = "échec de la sauvegarde"
f.code = 7
TRY
THROW f
CATCH err
Console(err.message + " / " + Str(err.code)) ' échec de la sauvegarde / 7
ENDTRY
THROWsans valeur est une erreur de compilation : « THROW requires a value ».- Une erreur levée quitte toutes les fonctions sur son chemin jusqu'à ce qu'un
CATCHla prenne : une fonction peut donc lever, et son appelant attraper. namevaut toujoursErrorpour unTHROW.
OPTION EXPLICIT
GLOBAL attempts AS integer = 0
GLOBAL connected AS integer = 0
FUNCTION Connect()
attempts = attempts + 1
IF attempts < 3 THEN THROW "délai dépassé"
ENDFUNCTION
WHILE connected = 0
TRY
Connect()
connected = 1
CATCH problem$
Console("essai " + Str(attempts) + " : " + problem$)
ENDTRY
ENDWHILE
Console("connecté après " + Str(attempts)) ' connecté après 3
⚠️ Ne relancez pas avec THROW l'erreur que vous avez attrapée.
THROW failure fabrique une nouvelle erreur à partir de son message et de son
code : le name devient Error et la command est perdue. Pour transmettre la
même erreur, utilisez RETHROW.
RETHROW et les blocs imbriqués#
RETHROW
RETHROW transmet l'erreur en cours de traitement au TRY extérieur suivant,
sans la changer.
TRY
TRY
THROW "disque plein"
CATCH inner$
Console("intérieur : " + inner$)
RETHROW
FINALLY
Console("finally intérieur")
ENDTRY
CATCH outer
Console("extérieur : " + outer.message)
ENDTRY
Le programme affiche intérieur : disque plein, puis finally intérieur, puis
extérieur : disque plein. Le FINALLY intérieur s'exécute avant le CATCH
extérieur.
RETHROWn'est permis que dans unCATCH(ou dans un bloc imbriqué dans unCATCH). Ailleurs : « RETHROW requires an active CATCH block ».- Il ne prend pas de valeur : « RETHROW does not accept a value ».
- Les blocs
TRYs'imbriquent à n'importe quelle profondeur. Une erreur va auCATCHenglobant le plus proche.
END et GOTO passent au travers#
END et GOTO ne sont pas des erreurs, et un CATCH ne les voit jamais.
FINALLY s'exécute quand même au passage.
TRY
Console("avant")
END
CATCH e
Console("END attrapé")
FINALLY
Console("finally s'exécute")
ENDTRY
Console("jamais")
Le programme affiche avant, puis finally s'exécute, et s'arrête. Un GOTO
vers une étiquette hors du bloc se comporte de même : FINALLY s'exécute, puis
le saut a lieu.
Quand une commande échoue : ErrorMode#
Une commande du catalogue peut échouer : un fichier absent, un identifiant qui
n'existe pas, une valeur hors limites. ErrorMode décide de la suite.
ErrorMode(<mode>)
ErrorMode()
| Mode | Valeur | Ce que fait une commande qui échoue |
|---|---|---|
#AK_ERROR_STOP | 2, la valeur par défaut | arrête le programme, ou passe au CATCH du TRY englobant |
#AK_ERROR_REPORT | 1 | écrit l'erreur dans la console, rend une valeur neutre, et le programme continue |
#AK_ERROR_IGNORE | 0 | rend une valeur neutre sans rien dire, et le programme continue |
La valeur neutre est 0 pour une commande qui rend un nombre, "" pour une
commande qui rend une chaîne, rien sinon.
ErrorMode(#AK_ERROR_IGNORE)
value = MemblockByte(999, 0)
Console(Str(value)) ' 0
IF ErrorOccurred() THEN Console("échec : " + LastError()) ' échec : Classic BASIC memory block 999 does not exist
Console(Str(ErrorMode())) ' 0
ErrorMode()sans argument rend le mode en cours.- Le mode reste en vigueur jusqu'au prochain
ErrorMode(...). - Toute autre valeur est une erreur : « Classic BASIC error mode must be #AK_ERROR_IGNORE, #AK_ERROR_REPORT, or #AK_ERROR_STOP ».
ErrorModecouvre les commandes du catalogue, et rien d'autre. UnTHROW, une division par zéro avecDIV, une poignée libérée ou une commande appelée avec un mauvais nombre d'arguments lèvent une erreur dans tous les modes.
ErrorMode(#AK_ERROR_IGNORE)
a = 1
b = 0
TRY
c = a DIV b
CATCH e$
Console("attrapé : " + e$) ' attrapé : BASIC integer division by zero
ENDTRY
Quelques commandes finissent leur travail après avoir rendu la main : la
feuille de partage, le sélecteur de fichier, l'ouverture d'un onglet. Si elles
échouent, il est trop tard pour arrêter le programme. Elles renseignent
LastError et ErrorOccurred, et écrivent dans la console sauf en mode
#AK_ERROR_IGNORE.
LastError et ErrorOccurred#
| Commande | Rend |
|---|---|
LastError() | le message du dernier échec de commande, sans le préfixe BASIC …() failed: ni le conseil. Il reste jusqu'au prochain échec. |
ErrorOccurred() | 1 si une commande a échoué depuis l'appel précédent, puis revient à 0 |
Les deux sont renseignés dans tous les modes, #AK_ERROR_STOP compris. Vous
pouvez aussi les lire dans un CATCH.
TRY
value = MemblockByte(999, 0)
CATCH
ENDTRY
Console(Str(ErrorOccurred())) ' 1
Console(Str(ErrorOccurred())) ' 0
Console(LastError()) ' Classic BASIC memory block 999 does not exist
⚠️ Lisez ErrorOccurred() une seule fois. Le deuxième appel répond déjà
0. Rangez le résultat si vous en avez besoin deux fois.
Les erreurs d'exécution que lève le langage#
En plus des commandes et de THROW, quelques opérations lèvent une erreur
d'elles-mêmes :
| Opération | Message |
|---|---|
a DIV 0 | BASIC integer division by zero |
ranger 1 / 0 dans une variable AS integer | BASIC integer requires a finite value |
FOR i = 1 TO 5 STEP 0 | BASIC FOR STEP cannot be zero |
GetRef ou SetRef sur une poignée libérée | BASIC reference handle has been released |
| une commande appelée avec un nombre d'arguments qu'elle n'accepte pas | BASIC classique command <name> received <n> argument(s); expected … |
1 / 0 sur une variable sans type ne lève pas d'erreur : cela donne
Infinity.
Où va une erreur non attrapée#
| Programme | Ce que fait une erreur d'exécution non attrapée |
|---|---|
un programme linéaire, ou un DO … LOOP | arrête le programme à cette ligne |
une fonction du cycle de vie (update, onCollisionEnter…) | signale l'erreur. L'Engine rappelle la fonction à l'image ou à l'événement suivant, donc l'erreur se répète |
Voir Les fonctions pour les fonctions du cycle de vie, et La boucle de jeu et les tâches pour les erreurs dans une tâche.
Comparé aux dialectes classiques#
| Dialecte | Ce qu'il a | Dans aukimi |
|---|---|---|
| AGK | SetErrorMode avec 0, 1, 2 ; GetLastError, GetErrorOccurred | mêmes modes et mêmes valeurs, sans le verbe : ErrorMode(...), LastError(), ErrorOccurred() |
| DarkBASIC Pro | pas d'erreurs structurées | TRY / CATCH / FINALLY |
| PureBasic | OnErrorCall, OnErrorGoto | TRY / CATCH, un gestionnaire par bloc au lieu d'un par programme |
| Visual Basic | On Error Resume Next, On Error GoTo | ErrorMode(#AK_ERROR_IGNORE) pour les commandes, TRY pour le reste |