Un moteur destiné à piloter des sas, des drones et des postes de survie ne peut pas se contenter d'exécuter correctement les programmes corrects. Il doit surtout borner les programmes incorrects : ceux qui tentent de sortir de leur périmètre, et ceux qui ne s'arrêtent jamais. CakeLang dispose de deux mécanismes pour cela, et ils se vérifient tous deux par l'expérience.
Dans l'éditeur, les fonctions d'accès aux fichiers, à l'environnement et au système ne sont tout simplement pas enregistrées. Un programme qui les appelle ne les trouve pas.
t09c07-a.cake|> "Debut de mission"%
Cake.writeFile("journal.txt", "tentative d'ecriture")%
|> "Mission poursuivie"%
Debut de mission Mission poursuivie
cake: statement ignore -> "Cake.writeFile("journal.txt", "tentative d'ecriture")" (Fonction 'Cake.writeFile' non trouvee)<projet nom="t09c07-a" package="com.unibool.script" type="Library">
<classe nom="Root" visibilite="Public">
<methode nom="main" retour="void" visibilite="Public">
<bloc>
<appel methode="println" args=""Debut de mission""/>
<code-brut><![CDATA[Cake.writeFile("journal.txt", "tentative d'ecriture")%]]></code-brut>
<appel methode="println" args=""Mission poursuivie""/>
</bloc>
</methode>
</classe>
</projet|> entrées/sorties · bibliothèque : Cake.writeFile0271c515dc7b5563 (reproductible : cake hash t09c07-a.cake)Observez le diagnostic : l'instruction est ignorée, et le programme continue. C'est un choix de conception à connaître. Il rend le bac à sable sûr — rien n'a été écrit — mais il ne l'arrête pas. Pour un système critique, la bonne pratique est de traiter tout message « instruction ignorée » comme un échec de validation avant mise en service, et non comme un simple avertissement.
La seconde borne porte sur le temps. Chaque boucle est limitée à un million d'itérations, et un compteur partagé par toute l'exécution empêche que deux boucles imbriquées, chacune sous sa limite, ne tournent indéfiniment ensemble. L'abandon est déclenché par l'interpréteur lui-même, et il ne peut pas être capturé par le programme : un script ne peut pas avaler son propre arrêt.
t09c07-b.cake=: n=0%
$= (true) {
:+ n=1%
}
|> "Cette ligne ne sera jamais atteinte"%
CakeRuntimeException: while: limite d'iterations atteinte (1M)
<projet nom="t09c07-b" package="com.unibool.script" type="Library">
<classe nom="Root" visibilite="Public">
<methode nom="main" retour="void" visibilite="Public">
<bloc>
<declaration nom="n" type="var" valeur="0"/>
<tant-que condition="true">
<bloc>
<affectation cible="n" valeur="n + 1"/>
</bloc>
</tant-que>
<appel methode="println" args=""Cette ligne ne sera jamais atteinte""/>
</bloc>
</methode>
</classe>
</projet=: déclarations · $= flux · :+ déclarations · |> entrées/sortiesd84300ca1d357712 (reproductible : cake hash t09c07-b.cake)Ici, contrairement au bac à sable, le moteur s'arrête net : la dernière ligne n'est jamais exécutée. Les deux comportements sont cohérents avec leur rôle. Une fonction interdite ne menace que ce qu'elle aurait touché ; une boucle infinie menace tout le reste du système.
Un programme critique ne doit pas seulement subir les refus : il doit pouvoir y réagir. Le bloc d'essai !{ … !} protège une suite d'instructions ; si l'une échoue, l'exécution passe à la capture (e) { … }, qui reçoit le message ; le bloc !~ { … } s'exécute dans tous les cas. À l'intérieur d'un essai, un refus du bac à sable n'est plus ignoré : il devient une erreur que le programme traite.
t09c07-c.cake!{
|> "Ecriture du journal de bord"%
Cake.writeFile("journal.txt", "vol 12 nominal")%
|> "Journal ecrit"%
!} (e) {
|> "Refus : journal conserve en memoire"%
}
!~ {
|> "Fin de sequence"%
}
Ecriture du journal de bord Refus : journal conserve en memoire Fin de sequence
<projet nom="t09c07-c" package="com.unibool.script" type="Library">
<classe nom="Root" visibilite="Public">
<methode nom="main" retour="void" visibilite="Public">
<bloc>
<essai>
<corps>
<bloc>
<appel methode="println" args=""Ecriture du journal de bord""/>
<code-brut><![CDATA[Cake.writeFile("journal.txt", "vol 12 nominal")%]]></code-brut>
<appel methode="println" args=""Journal ecrit""/>
</bloc>
</corps>
<attraper exception="Exception" variable="e">
<bloc>
<appel methode="println" args=""Refus : journal conserve en memoire""/>
</bloc>
</attraper>
<finalement>
<bloc>
<appel methode="println" args=""Fin de sequence""/>
</bloc>
</finalement>
</essai>
</bloc>
</methode>
</classe>
</projet!{ erreurs · |> entrées/sorties · !} erreurs · !~ erreurs · bibliothèque : Cake.writeFilef0b9cd731d34dbcd (reproductible : cake hash t09c07-c.cake)La troisième ligne du bloc n'est jamais atteinte, la capture prend le relais, et le bloc final ferme la séquence. C'est le schéma à suivre pour tout accès à une ressource que l'environnement peut refuser.
Un environnement d'exécution se juge sur les programmes qu'il empêche. Le bac à sable borne l'espace, le budget borne le temps, et le bloc d'essai transforme un refus en décision.
Le chapitre suivant montre comment l'empreinte d'arbre permet de figer, reproduire et suivre des milliers de transformations.