Hlavní navigace

Bezpečné programování ala Ada

Marek Paška

V tomto článku popíši bezpečné programování podporované kvalitním programovacím jazykem. Tím jazykem je Ada, protože ta má tvorbu bezpečného kódu přímo v popisu práce. Ada je přesný opak jazyků s "volnější syntaxí", jako jsou PHP či Perl. Na rozdíl od nich zavádí silný typový systém a řadu kontrol, a to jak za běhu, tak především při překladu.

Kdo je Ada?

Ada je programovací jazyk, který vznikl na začátku 80. let z potřeby amerického ministerstva obrany (DoD) vytvořit jeden univerzální jazyk, který by byl použitelný jak pro vložená (embedded) zařízení, tak pro velké systémy s milióny řádky kódu. Byl pojmenován po jisté Augustě Adě Byron, která spolupracovala s Charlesem Babbagem a pravděpodobně byla prvním programátorem na světě. Ada má „pascaloidní“ syntaxi a rozhodně se nejedná o žádnou divokou exotiku, ale o vcelku standardní nástroj zaměřený na tvorbu spolehlivých a přenositelných aplikací. Současná verze Ady se nazývá Ada95 a obsahuje mimo jiné prostředky pro OOP (ovšem jedná se o velice konzervativní „vojenský“ návrh, funkčně tak na úrovni C++ s jednoduchou dědičností, bezpečnostně samozřejmě mnohem výš :-), luxusní jazykové konstrukce pro vícevláknové programování a generické konstrukce.

Ada je poměrně komplexní a málo rozšířený jazyk. Ona malá rozšířenost je pravděpodobně způsobena její „nepříjemností“ pro začátečníky, protože definuje mnohá omezení, ve kterých se programátor musí dobře vyznat, aby si o ně „nenamlátil“. Tato omezení obvykle ocení až zkušenější programátoři pracující na velkých projektech. Nejčastěji se používá pro tvorbu mission/safety critical aplikací. Několik příkladů: řídící software k raketoplánům, jaderným elektrárnám, vlakům TGV, stíhačce Gripen atd. Já osobně jsem v Adě implementoval FTP server, což je dle mého soudu aplikace pro Adu jako stvořená.

Asi nejznámější překladač Ady je GNAT. Je napsán v Adě :-), dostupný pod GPL a pro všechny myslitelné i nemyslitelné platformy.

Tento článek si rozhodně nedělá ambice naučit někoho programovat v Adě. Ada se dá nejlépe naučit přečtením nějaké vhodné tlusté knihy. Ale aby každý viděl, že Ada je úplně obyčejný jazyk, předvedu jedno malé Ahoj světe.

with Ada.Text_Io;
use Ada.Text_Io;

procedure Hello_Main is
begin
  Put_Line("Hello world!");
end

Silný typový systém

Řada programátorů si myslí, že to, co nabízí jazyk C, je typová kontrola. V porovnání s různými skriptovacími jazyky sice jistou kontrolu typů při překladu nabízí, ovšem automatické konverze dávají typové bezpečnosti pořádně na frak. Dokonce ani taková Java nedokáže detekovat přetečení celého čísla, Object Pascal z Delphi umožňuje detekovat přetečení a dokonce má intervalové typy, ale to je jen polovičaté řešení.

Právě důmyslný silný typový systém a zakázání automatických konverzí je cestou k rychlejšímu, levnějšímu vývoji bezpečnějších a spolehlivějších aplikací. Typový systém Ady je založen na tom, že jednotlivé proměnné různých typů nelze mezi sebou implicitně konvertovat, ale lze tak činit explicitně. Navíc zavádí tzv. subtypy.

Například standardní 32bitový celočíselný typ by mohl být definován nějak takto:

type Integer is range -2**31 .. 2**31-1;

Tedy nic překvapivého. Překladač pro daný rozsah určí nejvhodnější reprezentaci a kontroluje přetečení. Zajímavějším se to stane, pokud začneme vytvářet specializovanější datové typy pro nějaký konkrétní účel. Například:

type Jablka is new Integer;
type Hrusky is new Integer;

Jedná se o obdobu céčkovského typedef. Ovšem tyto konstrukce nezavádí nové pojmenování existujícího typu, nýbrž typy nové. Proměnné typů Integer, Jablka a Hrusky jsou spolu nekompatibilní. Následující konstrukce způsobí chybu při překladu.

declare
  Pocet_Hrusek : Hrusky := 10;
  Pocet_Jablek : Jablka;
begin
  pocet_jablek := pocet_hrusek; --nelze přiřazovat hrušky a jablka! (přestože mají stejnou fyzickou reprezentaci)
end;

Takže nám jazyk zaručí, že nikde v programu nezaměníme hrušky a jablka. Pokud bychom to přece jenom potřebovali, můžeme si to explicitně poručit. Následující příklad však není programátorská chybka z nepozornosti, ale buď čirý úmysl, nebo čirá sabotáž.

Pocet_Jablek := Jablka(Pocet_Hru­sek);

Integritní omezení

Integritní omezení pomocí intervalů celočíselných typů jsou velice užitečná pro eliminaci logických chyb v programu. Pokud například dopředu víme, že v programu nikdy nebudeme mít víc jak 100 jablek a víc jak 50 hrušek a budeme chtít vyloučit záporné počty plodů (malvic :-), uděláme to takto.

type Jablka is new Integer range 0..100;
type Hrusky is new Integer range 0..50;

Následující řádek způsobí chybu při překladu:

Pocet_Jablek := 101;

Při konverzi typů se kontrolují hodnoty proměnných, a pokud by mělo dojít k porušení integritních pravidel, vygeneruje se výjimka. Následující kousek kódu tedy projde, jenom pokud je Pocet_Jablek menší než padesát.

Pocet_Hrusek := Hrusky(Pocet_Jablek); --může vyvolat výjimku typu Constraint_Error (chyba mezí)

Příklad s ovocem byl sice názorný, ale poněkud nepraktický. Dovolím si proto ještě jednu ukázku, která demonstruje standardní Adou definované celočíselné typy. Tentokrát zapojíme adovskou specialitu, a tou jsou podtypy (subtypy).

type Integer is range -2**31 .. 2**31-1;
subtype Natural is Integer range 0 .. 2**31-1; --podtyp přirozené číslo (s nulou)
subtype Positive is Integer range 1 .. 2**31-1; -- podtyp kladné číslo

Subtypy jsou užitečné tam, kde chceme omezit rozsah nějakého typu, ale nechceme zavádět typ nový. Všechny proměnné určitého typu a jeho subtypů mohou být mezi sebou libovolně přiřazovány bez jakýchkoli explicitních konverzí (přetypování). Generovaný kód samozřejmě stále kontroluje hodnoty a v případě překročení mezí vygeneruje výjimku.

Pokud nyní budu programovat například spojový seznam, nikdy nenadeklaruji proměnnou Pocet_Atomu (označující počet prvků v seznamu) jako typ Integer, nýbrž jako Natural. Přidám tím do programu další informaci a omezím možnost vzniku chyby. Nikdy neprojde odebrání atomu z prázdného seznamu, nikdy nebude počet atomů menší než nula. Toto je opravdový příklad ze života, kdy mi překladač odhalil chybu, která by v C mohla skončit nějakým veselým Access Violation nebo Segmentation Fault. A já jsem tehdy mohl jít včas spát :-).

Ještě poznamenám, že Ada umožňuje definovat intervaly i u floating-point typů. Užitečné to může být třeba pro úhly v radiánech:

type Uhel is new Float range 0.0 .. 2.0 * pi;

Další kouzla

Představte si, že máme nějakou aplikaci (třeba software k vesmírné sondě :-), která počítá plochy nějakých objektů. Chceme definovat integritní omezení v duchu fyzikálních zákonů.

type Metry is new Float;
type Ctverecni_Metry is new Float;

Když teď budeme chtít spočítat nějakou plochu, budeme muset explicitně přetypovávat. Což je poněkud neelegantní.

declare
  vyska : Metry := 10.0;
  sirka : Metry := 15.0;
  plocha : Ctverecni_Metry;
begin
  plocha := Ctverecni_Metry(vyska*sirka);--musíme přetypovávat
end;

Ale my víme, že když násobíme metry, výsledek je vždy ve čtverečních metrech. Nešlo by takové pravidlo šikovně zadrátovat do jazyka? Šlo! Vyřešíme to přetížením operátoru násobení pro datový typ metry tak, aby vracel metry čtvereční.

function "*" (Left, Right : Metry) return Ctverecni_Metry is
begin
  return Ctverecni_Metry(Float(Left)*Float(Right));
  -- před násobením jsme přetypovali na float, abychom zabránili rekurzi
  -- takto donutíme překladač použít standardní násobení pro typ Float
end;

Nejen, že teď při výpočtu plochy nemusíme přetypovávat, ale následující (fyzikálně nekorektní) konstrukce dokonce způsobí chybu při překladu. A to je přesně to, co jsme chtěli.

declare
  vyska : Metry := 10.0;
  sirka : Metry := 15.0;
  plocha : Metry;
begin
  plocha := vyska*sirka; --zde nastane chyba prekladu
end;

Co to stojí?

Řada programátorů jistě namítne, že po použití takovýchto konstrukcí musí být výsledné programy pomalé a velké. Není to pravda. Spousty kontrol se provádějí při překladu zdrojového kódu (protože to je to, co zrychluje vývoj) a run-time kontroly jsou jen tam, kde je to potřeba. Je nasnadě, že když přiřazujete proměnnou typu přirozené číslo do proměnné typu celé číslo, není žádná kontrola potřeba (celá čísla jsou nadmnožina přirozených čísel. Navíc tím, že různými omezeními přidáte do zdrojáku informace, umožníte progresivní high-level optimalizace, které by jinak nebyly možné. Překladač GNAT je postaven na back-endu z GCC, takže disponuje rovněž kvalitními low-level optimalizacemi. Adovské programy snesou rychlostní srovnání s C/C++. Pro kontrolu celočíselných mezí existuje v současných procesorech speciální instrukce, takže režie s tím spojená se u reálných aplikací počítá na jednotky procent. Ostatně dnes jsou v matematických operacích velice rychlé i Java a C#.

Závěrem

Doufám, že po přečtení tohoto článku každý tuší, co je to silný typový systém a jak taková věc může programování zpříjemnit. Při vývoji aplikací, kde jde o lidské životy, je taková podpora ze strany jazyka takřka nutností, při vývoji běžných aplikací to šetří peníze a nervy. Ada nabízí pro šetření nervů ještě mnohem víc. Například atributy typů užitečné pro zamezení indexování pole mimo jeho rozsah, thread-safe objekty, důmyslné skrývání implementace či překladačem určovaný způsob předávání parametrů podprogramům.

Našli jste v článku chybu?

7. 10. 2003 2:22

agent (neregistrovaný)

Děkuji za vjednání míru. Je v tom kus pravdy. Snad bych jen dodal, že útočím na jazyk, nikoli na člověka.

Snad bych jen upřesnil, že v OP jsem byl nucen dělat více, než je zdrávo, a rozhodně jsem si ho jako jazyk neoblíbil. Možná ani ne tak kvůli jazyku samotnému, který zase není až tak zlý, ale kvůli přístupu firmy Borland. IMHO programovací jazyk nedělá ani tak syntaxe a možnosti, jako jeho základní knihovna. A jako celek, tj. jazyk OP a základní knihovna nad něj působí dost nekonzistentně,…

6. 10. 2003 22:45

Cohen (neregistrovaný)

Nevim, jestli jsem to dobre pochopil, ale souhlasim s tim, ze Qmail je velmi dobry priklad prasacky napsaneho kodu ;-).

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Vitalia.cz: Nestlé vyvinulo nový typ „netloustnoucího“ cukru

Nestlé vyvinulo nový typ „netloustnoucího“ cukru

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

Podnikatel.cz: Zavře krám u #EET Malá pokladna a Teeta?

Zavře krám u #EET Malá pokladna a Teeta?

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

Vitalia.cz: Jmenuje se Janina a žije bez cukru

Jmenuje se Janina a žije bez cukru

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: Taky věříte na pravidlo 5 sekund?

Taky věříte na pravidlo 5 sekund?

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech