Obsah
1. Programovací jazyk Clojure 12: překlad programů napsaných v Clojure do bajtkódu JVM
2. Jednoduché datové typy Clojure
3. Strukturované datové typy – kolekce
4. Práce s poli programovacího jazyka Java
5. Zjednodušená konstrukce polí
7. Překlad funkcí Clojure do bajtkódu JVM
8. Obsah vygenerovaných souborů .class
1. Programovací jazyk Clojure 12: překlad programů napsaných v Clojure do bajtkódu JVM
V dnešní části seriálu o Javě i o virtuálním stroji Javy si řekneme, jakým způsobem se aplikace napsané v programovacím jazyku Clojure mohou přeložit do bajtkódu JVM. Výsledkem překladu programů napsaných v Clojure je ve všech případech bajtkód (uložený v souborech s koncovkou .class), jenž je použitelný na prakticky libovolném virtuálním stroji Javy, což může být poněkud překvapivé, když si uvědomíme, že Clojure je – na rozdíl od Javy – funkcionální a dynamicky typovaný programovací jazyk. Nicméně právě na příkladu Clojure je možné ukázat, že bajtkód JVM lze kupodivu využít i tímto poněkud netypickým způsobem. Při vývoji nových aplikací většinou není nutné se nijak zabývat překladem jednotlivých zapisovaných forem (vyhodnocovaných výrazů), protože tento překlad za nás automaticky provádí samotný interpret jazyka Clojure – formy a tím pádem i programy tudíž nejsou přímo interpretovány, ale nejdříve jsou přeloženy do bajtkódu a posléze spuštěny (bajtkód se v tomto případě dynamicky zavádí do JVM, není tedy nutné generovat externí soubory .class).
Ovšem ve chvíli, kdy se má aplikace nainstalovat na počítače klientů či například nasadit na aplikační server, je vhodnější dodat administrátorům již přeložené soubory .class nebo ještě lépe javovský či webový archiv s aplikací (.jar, .war). Ještě než se budeme zabývat vlastním způsobem překladu programů z Clojure do bajtkódu, musíme si říci (a částečně tak zopakovat již v předchozích článcích zmíněné informace), jakým způsobem jsou reprezentována data, s nimiž se v Clojure pracuje. Vzhledem k tomu, že programy v Clojure jsou překládány do bajtkódu (ať již explicitně na žádost programátora či implicitně interpretrem), musí jakýkoli objekt, s nímž se v aplikaci pracuje, mít i odpovídající rozhraní či třídu v Javě, protože v JVM se pro volání metod používají instrukce vypsané v následující tabulce, které vyžadují, aby existovala jednoznačná signatura volané metody, v níž jsou mj. zapsány i typy předávaných parametrů a současně i typ návratové hodnoty:
# | Instrukce | Opkód | Operandy | Prováděná operace |
---|---|---|---|---|
1 | invokestatic | 0×B8 | highbyte, lowbyte | zavolání statické metody s předáním parametrů této metodě |
2 | invokevirtual | 0×B6 | highbyte, lowbyte | zavolání nestatické metody s předáním hodnoty this a všech dalších parametrů |
3 | invokespecial | 0×B7 | highbyte, lowbyte | zavolání speciální metody, většinou konstruktoru |
4 | invokeinterface | 0×B9 | highbyte, lowbyte, count | zavolání metody deklarované v rozhraní, samozřejmě s předáním parametrů |
2. Jednoduché datové typy Clojure
V předchozích částech tohoto seriálu jsme si řekli, jakým způsobem se v programovacím jazyku Clojure pracuje se základními datovými typy, které jsou nedílnou součástí tohoto jazyka, resp. přesněji řečeno jsou součástí jeho jádra, i když to je relativně snadno modifikovatelné. Jedná se o jednoduché datové typy, mezi něž patří například numerické hodnoty (ty se dále dělí na celá čísla, čísla s plovoucí řádovou čárkou, zlomky a čísla typu BigDecimal a BigInt – viz další text), pravdivostní hodnoty, řetězce, jednotlivé znaky a taktéž takzvané klíče (keywords). Hodnotu jakéhokoli zmíněného jednoduchého datového typu lze získat buď zápisem jejího literálu (tj. z jazykového hlediska „konstanty“), popř. ji lze získat ve formě výsledku nějaké funkce nebo speciální formy. Vzhledem k tomu, že hodnoty jednoduchých datových typů lze použít jako parametry při volání metod javovských tříd či je naopak získat jako výsledek volání nějaké metody, je v následující tabulce zapsáno i jméno příslušného rozhraní (interface) či přímo implementované třídy programovacího jazyka Java:
# | Typ | Literál v Clojure | Implementovaná třída či rozhraní v Javě |
---|---|---|---|
1 | Boolean | true | java.lang.Boolean |
2 | Number | 42 | java.lang.Number |
3 | String | „string“ | java.lang.String |
4 | Character | \x | java.lang.Character |
5 | Keyword | :klíč | clojure.lang.Keyword |
Z předchozí tabulky vyplývá, že numerické hodnoty jsou vždy reprezentovány objektem, který je instancí třídy implementující rozhraní java.lang.Number. O jaké konkrétní třídy se jedná, nám prozradí druhá tabulka umístěná pod tímto odstavcem. Za zmínku stojí především fakt, že tvůrci programovacího jazyka Clojure preferují využívání co nejmenšího množství javovských tříd (či spíše jejich instancí), takže se nesetkáme například s instancemi tříd java.lang.Short, java.lang.Integer či java.lang.Float, což je docela praktické a dobře to odpovídá filozofii vysokoúrovňových jazyků (podobně je tomu i v implementaci JavaScriptu – viz též projekt Rhino):
# | Literál v Clojure | Implementovaná třída či rozhraní v Javě | Poznámka |
---|---|---|---|
1 | 42 | java.lang.Long | desítková soustava |
2 | 16RFF | java.lang.Long | číselná soustava zapsaná za znakem „R“ |
3 | 42.0 | java.lang.Double | běžná hodnota reprezentovaná v systému plovoucí řádové čárky |
4 | 1/2 | clojure.lang.Ratio | prováděné operace vždy zlomek zjednoduší |
5 | 42M | java.math.BigDecimal | lze aplikovat běžné funkce jako na další numerické hodnoty |
6 | 42N | clojure.lang.BigInt | lze aplikovat běžné funkce jako na další numerické hodnoty |
3. Strukturované datové typy – kolekce
Kromě jednoduchých datových typů popsaných v předchozí kapitole se však v Clojure samozřejmě používají i strukturované datové typy představované různými kolekcemi. Pokud prozatím zůstaneme a nativních kolekcí podporovaných již v jádru tohoto jazyka, může každý vývojář používající programovací jazyk Clojure pracovat se seznamy (což asi očekáváme od všech LISPovských jazyků), vektory, mapami a množinami. Většina složitějších datových struktur se vytváří právě s využitím seznamů, vektorů, map a množin, protože to dobře odpovídá filozofii dynamicky typovaných funkcionálních jazyků, u nichž mají programátoři k dispozici velké množství univerzálních funkcí akceptujících jako své parametry právě čtyři výše zmíněné strukturované datové typy, resp. jakoukoli sekvenci (u objektově orientovaných jazyků je naproti tomu funkcionalita navázána pouze na konkrétní datový typ – třídu). Ve třetí tabulce jsou všechny čtyři základní typy datové typy Clojure vypsány i s odpovídajícími literály:
# | Typ kolekce | Zápis | Konstruktor | Implementovaná třída |
---|---|---|---|---|
1 | Seznam | (prvky) | (prvky) | clojure.lang.PersistentList |
2 | Vektor | [prvky] | (vector prvky) | clojure.lang.PersistentVector |
3 | Mapa | {dvojice klíč-hodnota} | (hash-map dvojice klíč-hodnota) | clojure.lang.PersistentArrayMap |
4 | Množina | #{unikátní prvky} | (hash-set unikátní prvky) | clojure.lang.PersistentHashSet |
Všechny čtyři výše zmíněné typy kolekcí jsou sice implementovány třídami z balíčku clojure.lang, současně ovšem implementují i některá rozhraní, která jsou součástí standardního API programovacího jazyka Java. S následující tabulkou jsme se sice již setkali, ale možná nebude na škodu si zopakovat, která rozhraní jsou základními kolekcemi implementována a ve kterých případech je tedy možné tyto kolekce v Javě využít:
Rozhraní | seznam | vektor | mapa | množina |
---|---|---|---|---|
java.util.Collection | ano | ano | ne | ano |
java.util.List | ano | ano | ne | ne |
java.util.Map | ne | ne | ano | ne |
java.util.Set | ne | ne | ne | ano |
java.util.RandomAccess | ne | ano | ne | ne |
java.lang.Iterable | ano | ano | ano | ano |
java.lang.Comparable | ne | ano | ne | ne |
Na závěr této kapitoly si připomeňme, že v programovacím jazyce Clojure jsou všechny kolekce neměnitelné – immutable a každý pokus o jejich změnu z Javy, například zavolání metody Collection.add(), Collection.remove() či dokonce Collection.clear() povede ke vzniku běhové výjimky (runtime exception).
4. Práce s poli programovacího jazyka Java
Čtenářům tohoto seriálu asi není zapotřebí připomínat, že v programovacím jazyku Java je možné vytvářet pole, tj. homogenní datové struktury, které mohou obsahovat buď hodnoty primitivních datových typů nebo naopak reference na objekty (vytvořené na haldě). Pole jsou vytvářena dynamicky, tj. velikost jejich dimenzí může být vypočtena až v době běhu programu (runtime). Prvky uložené v polích jsou navíc měnitelné – mutable. Tato poslední vlastnost je z hlediska programovacího jazyka Clojure nevýhodná, ovšem práci s poli se v Clojure v některých případech nevyhneme, a to ze dvou důvodů. První důvod je ten, že mnoho metod ze standardního API Javy jako svůj argument akceptuje právě pole, popř. je pole návratovou hodnotou nějaké metody. Druhý důvod spočívá v tom, že práce s poli, zejména s poli primitivních datových typů (pole celých čísel atd.), je v mnoha algoritmech velmi efektivní, a to jak z hlediska využití operační paměti, tak i z hlediska rychlého přístupu k prvkům polí přes indexy a navíc s využitím speciálních instrukcí bajtkódu:
# | Instrukce | Opkód | Operandy | Prováděná operace |
---|---|---|---|---|
1 | newarray | 0×BC | arraytype | Vytvoří nové pole s prvky primitivního datového typu |
2 | anewarray | 0×BD | highbyte, lowbyte | Vytvoří nové pole objektů |
3 | multianewarray | 0×C5 | highbyte, lowbyte, dimensions | Vytvoří vícedimenzionální pole o dimensions dimenzích |
4 | iaload | 0×2E | × | Přečtení prvku z pole typu int[] |
5 | laload | 0×2F | × | Přečtení prvku z pole typu long[] |
6 | faload | 0×30 | × | Přečtení prvku z pole typu float[] |
7 | daload | 0×31 | × | Přečtení prvku z pole typu double[] |
8 | aaload | 0×32 | × | Přečtení prvku z pole typu reference[] |
9 | baload | 0×33 | × | Přečtení prvku z pole typu byte[] nebo boolean[] |
10 | caload | 0×34 | × | Přečtení prvku z pole typu char[] |
11 | saload | 0×35 | × | Přečtení prvku z pole typu short[] |
12 | iastore | 0×4F | × | Zápis nové hodnoty prvku do pole typu int[] |
13 | lastore | 0×50 | × | Zápis nové hodnoty prvku do pole typu long[] |
14 | fastore | 0×51 | × | Zápis nové hodnoty prvku do pole typu float[] |
15 | dastore | 0×52 | × | Zápis nové hodnoty prvku do pole typu double[] |
16 | aastore | 0×53 | × | Zápis nové hodnoty prvku do pole typu reference[] |
17 | bastore | 0×54 | × | Zápis nové hodnoty prvku do pole typu byte[] nebo boolean[] |
18 | castore | 0×55 | × | Zápis nové hodnoty prvku do pole typu char[] |
19 | sastore | 0×56 | × | Zápis nové hodnoty prvku do pole typu short[] |
Z tohoto důvodu se v následujícím textu seznámíme se základy práce s poli přímo z jazyka Clojure. Pro vytvoření nového pole s jednou dimenzí se používá funkce make-array, které se musí předat dva parametry: typ prvků pole a velikost pole. Typem je zde myšlena javovská třída, ovšem v případě, že se má vytvořit pole obsahující prvky některého primitivního datového typu, používá se namísto třídy zápis: jméno_obalové_třídy/TYPE (viz též JavaDoc k obalovým třídám primitivních datových typů). Podívejme se na několik demonstračních příkladů:
; desetiprvkové pole typu int user=> (make-array Integer/TYPE 10) #<int[] [I@1c5466b> ; stoprvkové pole typu double user=> (make-array Double/TYPE 100) #<double[] [D@d0220c> ; dvouprvkové pole typu boolean user=> (make-array Boolean/TYPE 2) #<boolean[] [Z@2d189c> user=>
Pole objektů, tj. pole obsahující reference na objekty, se vytváří stejně snadno:
; desetiprvkové pole typu Integer user=> (make-array Integer 10) #<Integer[] [Ljava.lang.Integer;@6ed322> ; stoprvkové pole typu Double user=> (make-array Double 100) #<Double[] [Ljava.lang.Double;@16917ee> ; pole obsahující tisíc řetězců user=> (make-array String 1000) #<String[] [Ljava.lang.String;@c2ee15> ; pole obsahující tisíc barev (instancí třídy java.awt.Color) user=> (make-array java.awt.Color 1000) #<Color[] [Ljava.awt.Color;@1fe571f> user=>
Vícerozměrná pole se vytváří jednoduše takovým způsobem, že se velikost všech dimenzí zapíše ihned za typ prvků pole:
; matice 3x3 prvky typu int user=> (make-array Integer/TYPE 3 3) #<int[][] [[I@18e8541> ; trojrozměrné pole 3x4x5 prvků typu Double user=> (make-array Double/TYPE 3 4 5) #<double[][][] [[[D@29c58e> ; dvourozměrné pole řetězců user=> (make-array String 10 10) #<String[][] [[Ljava.lang.String;@1c7980c> user=>
5. Zjednodušená konstrukce polí
Jak jsme si již řekli v předchozí kapitole, používají se pole i v těch případech, kdy je zapotřebí nějaký algoritmus (pracující například intenzivně s vektory a s maticemi) napsat co nejefektivnějším způsobem. Vzhledem k tomu, že se v těchto případech prakticky vždy využívají pole obsahující prvky primitivních datových typů, obsahuje programovací jazyk Clojure i několik funkcí umožňujících tato pole snadno vytvořit, protože použití funkce make-array je dosti nečitelné kvůli nutnosti zapisovat typ prvků pole. Alternativně je namísto funkce make-array možné použít následující funkce:
# | Funkce | Význam |
---|---|---|
1 | boolean-array | vytvoření pole obsahujícího prvky typu boolean |
2 | byte-array | vytvoření pole obsahujícího prvky typu char |
3 | char-array | vytvoření pole obsahujícího prvky typu char |
4 | short-array | vytvoření pole obsahujícího prvky typu short |
5 | int-array | vytvoření pole obsahujícího prvky typu int |
6 | long-array | vytvoření pole obsahujícího prvky typu long |
7 | float-array | vytvoření pole obsahujícího prvky typu float |
8 | double-array | vytvoření pole obsahujícího prvky typu double |
Všech sedm funkcí z předchozí tabulky existuje ve čtyřech variantách, tj. ve skutečnosti existuje 32 způsobů vytvoření pole obsahujících prvky vybraného primitivního datového typu. Ukažme si tyto možnosti na příkladu funkce int-array:
# | Zápis | Význam |
---|---|---|
1 | (int-array size) | vytvoření pole zadané velikosti |
2 | (int-array size value) | vytvoření pole zadané velikosti a naplnění všech prvků zvolenou hodnotou |
3 | (int-array collection) | vytvoření pole z kolekce (prvky kolekce jsou převedeny na int) |
4 | (int-array size collection) | jako předchozí funkce, ale zbývající prvky pole jsou naplněny nulami |
Nyní nám zbývá si ukázat několik jednoduchých demonstračních příkladů, v nichž se vytvoří pole prvků typu integer několika různými způsoby:
; vytvoření pole obsahujícího deset prvků typu int ; (prvky budou nulové) user=> (int-array 10) #<int[] [I@14e113b> ; vytvoření pole obsahujícího deset prvků typu int ; a inicializace všech prvků na hodnotu -1 user=> (int-array 10 -1) #<int[] [I@1236cd5> ; vytvoření čtyřprvkového pole a inicializace ; prvků pole ze zadané kolekce - vektoru user=> (int-array [1 2 3 4]) #<int[] [I@15d3388> ; vytvoření čtyřprvkového pole a inicializace ; prvků pole ze zadané kolekce - množiny user=> (int-array #{1 2 3 4}) #<int[] [I@15d3388> ; vytvoření čtyřprvkového pole a inicializace ; prvků pole ze zadané kolekce - seznamu user=> (int-array '(1 2 3 4)) #<int[] [I@1c54796> ; vytvoření tisíciprvkového pole a inicializace ; pouze prvních čtyř prvků pole ze zadané kolekce ; (ostatní prvky budou nulové) user=> (int-array 1000 [1 2 3 4]) #<int[] [I@d58939> user=>
6. Přístup k prvkům polí
Pole je možné taktéž vytvořit z libovolné kolekce s využitím funkcí to-array a to-array-2d. První z těchto funkcí vytvoří pole objektů typu Object (resp. instancí potomků této třídy) s využitím metody java.util.Collection.toArray(), druhá funkce je již interně poněkud složitější. Opět následuje několik demonstračních příkladů na využití funkce to-array a to-array-2d:
; vytvoření pětiprvkového pole user=> (to-array [1 2 3 4 5]) #<Object[] [Ljava.lang.Object;@8c4a77> ; vytvoření pole s tisíci prvky user=> (to-array (range 1 1000)) #<Object[] [Ljava.lang.Object;@13c6641> ; vytvoření matice 3x3 prvky user=> (to-array-2d [[1 2 3] [4 5 6] [7 8 9]]) #<Object[][] [[Ljava.lang.Object;@e35bb7> ; druhá dimenze se automaticky dopočítá user=> (to-array-2d [(range 1 10) (range 1 100) (range 1 1000)]) #<Object[][] [[Ljava.lang.Object;@186d484> user=>
Nyní si již můžeme popsat další funkce používané při práci s poli. Jedná se především o funkce pro čtení prvku a pro zápis nové hodnoty prvku. Ve skutečnosti existuje funkce pro zápis nové hodnoty prvku v několika variantách, protože kromě obecné funkce aset používané pro pole obsahující reference na objekty je k dispozici i osmice funkcí odpovídajících osmi primitivním datovým typům programovacího jazyka Java. Mezi další užitečné funkce patří funkce pro klonování pole a konečně i funkce pro zjištění velikosti pole:
# | Funkce | Parametry | Význam |
---|---|---|---|
1 | aget | pole index(y) | přečtení prvku z n-rozměrného pole |
2 | aset | pole index(y) hodnota | zápis prvku do pole obsahujícího reference na objekty |
3 | aset-boolean | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu boolean |
4 | aset-byte | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu byte |
5 | aset-char | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu char |
6 | aset-short | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu short |
7 | aset-int | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu int |
8 | aset-long | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu long |
9 | aset-float | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu float |
10 | aset-double | pole index(y) hodnota | zápis prvku do pole obsahujícího prvky typu double |
11 | aclone | pole | klonování pole |
12 | alength | pole | zjištění velikosti pole (resp. jedné jeho dimenze) |
Některé z těchto funkcí si můžeme ihned vyzkoušet:
; vytvoření desetiprvkového pole user=> (def pole (int-array 10)) #'user/pole ; nastavení hodnoty prvního prvku v poli user=> (aset pole 0 42) 42 ; přečtení hodnoty prvního prvku v poli user=> (aget pole 0) 42 ; nastavení hodnoty posledního prvku v poli user=> (aset-int pole 9 -1) -1 ; přečtení hodnoty posledního prvku v poli user=> (aget pole 9) -1 ; přečtení (implicitně nastavené) hodnoty druhého prvku v poli user=> (aget pole 1) 0 ; pokus o překročení indexu vede ke známé výjimce user=> (aget pole 10) ArrayIndexOutOfBoundsException 10 clojure.lang.RT.aget (RT.java:2094) ; přečtení délky pole user=> (alength pole) 10 ; klonování pole user=> (aclone pole) #<int[] [I@b3319f> user=>
Kromě těchto funkcí lze při práci s poli využít i poněkud složitější funkce se jmény amap a areduce, které si popíšeme příště.
7. Překlad funkcí Clojure do bajtkódu JVM
Nyní již máme velmi dobrou představu o tom, jakým způsobem programovací jazyk Clojure uchovává hodnoty jednoduchých i strukturovaných datových typů, ovšem ještě nevíme, jak provádí překlad funkcí (a popřípadě i multifunkcí, které jsme si ještě nepopisovali) do bajtkódu virtuálního stroje jazyka Java. V nejjednodušším případě, když se nesnažíme o vytváření plnohodnotných tříd a jejich instancí, ale o překlad jednotlivých funkcí, je každá funkce ve jmenném prostoru přeložena do zvláštního souboru .class, tj. každá funkce je „obalena“ svojí třídou. Navíc je pro každý jmenný prostor vytvořeno několik dalších pomocných tříd, které se starají o inicializaci a prvotní konfiguraci. Před popisem této relativně složité struktury se podívejme na to, jak lze provést překlad několika funkcí naprogramovaných v Clojure do bajtkódu JVM. Tyto funkce musí být uloženy v externím souboru (nelze provést překlad funkcí zapisovaných do interpretru) a navíc v tomto souboru musí být nastaven jmenný prostor s totožným názvem, jaký má jméno souboru.
Následující výpis je nutné uložit do souboru pojmenovaného Test.clj a uložit tento soubor do podadresáře classes, protože právě zde bude tento zdrojový soubor hledán při svém překladu:
(ns Test) (defn plus [x y] (+ x y)) (defn hello [] (println "Hello world!"))
Vidíme, že obsah není nijak komplikovaný – pouze se s využitím makra ns nastaví jmenný prostor Test (odpovídá názvu souboru Test.clj) a jsou zde deklarovány dvě funkce plus a hello.
Překlad je možné spustit přímo z interpretru, ovšem při jeho startu nesmíme zapomenout zařadit podadresář classes na CLASSPATH:
java -cp .;classes;clojure-1.4.0.jar clojure.main
Jakmile se interpret spustí, postačí zadat příkaz:
user=> (compile 'Test) Test user=>
který povede k vytvoření několika souborů s koncovkou .class v podadresáři classes.
8. Obsah vygenerovaných souborů .class
Podívejme se nyní podrobněji na to, jaké třídy se vlastně při překladu vytvořily. Ihned po překladu by měl obsah podadresáře classes obsahovat pětici souborů (jejich délky se mohou nepatrně lišit):
# | Soubor | Velikost | Popis |
---|---|---|---|
1 | Test.clj | 99 | zdrojový kód v Clojure |
2 | Test$loading__4784__auto__.class | 1472 | pomocné metody využívané „interpretrem“ |
3 | Test__init.class | 2647 | pomocné metody využívané „interpretrem“ |
4 | Test$plus.class | 865 | přeložený kód funkce plus |
5 | Test$hello.class | 828 | přeložený kód funkce hello |
V tuto chvíli nás budou zajímat především soubory Test$plus.class a Test$hello.class, protože ty obsahují „obalové“ třídy pro dvojici funkcí plus a hello. Interní struktura obou souborů je dosti podobná: najdeme zde jednu statickou proměnnou, statický blok, veřejný konstruktor a především pak metodu nazvanou invoke vracející instanci java.lang.Object. Počet parametrů této metody přesně odpovídá počtu parametrů obou funkcí, tj. v případě funkce hello nemá jí odpovídající metoda invoke žádné parametry a v případě funkce plus jde o metodu s dvojicí parametrů typu java.lang.Object, což je způsobeno tím, že jsme nikde blíže nedeklarovali, jakého typu mají být parametry funkce plus (i to je však v Clojure možné).
Výpis interní struktury třídy Test$hello (komentáře byly dopsány):
javap Test$hello Compiled from "Test.clj" public final class Test$hello extends clojure.lang.AFunction{ ; statická proměnná public static final clojure.lang.Var const__0; ; statický blok public static {}; ; veřejný konstruktor public Test$hello(); ; veřejná metoda invoke() bez parametrů public java.lang.Object invoke(); }
Výpis interní struktury třídy Test$plus:
javap Test$plus Compiled from "Test.clj" public final class Test$plus extends clojure.lang.AFunction{ ; statická proměnná public static final clojure.lang.Var const__0; ; statický blok public static {}; ; veřejný konstruktor public Test$plus(); ; veřejná metoda invoke() s dvojicí parametrů public java.lang.Object invoke(java.lang.Object, java.lang.Object); }
Na závěr si ještě ukážeme podrobnější strukturu třídy Test$plus, opět s ručně dopsanými komentáři:
javap -c Test$plus Compiled from "Test.clj" public final class Test$plus extends clojure.lang.AFunction{ public static final clojure.lang.Var const__0; ; statický blok public static {}; Code: 0: ldc #11; //String clojure.core 2: ldc #13; //String + 4: invokestatic #19; //Method clojure/lang/RT.var:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var; 7: checkcast #21; //class clojure/lang/Var 10: putstatic #23; //Field const__0:Lclojure/lang/Var; 13: return ; konstruktor v nemž se volá konstruktor třídy clojure.lang.AFunction public Test$plus(); Code: 0: aload_0 1: invokespecial #26; //Method clojure/lang/AFunction."<init>":()V 4: return ; Nejzajímavější část celého bajtkódu: ; metoda invoke() která reprezentuje naši funkci (plus [x y] (+ x y)) ; Funkce + ve skutečnosti odpovídá statické metodě clojure.lang.Numbers.add ; která dokáže sečíst jakákoli dvě čísla, tedy například i dva zlomky. public java.lang.Object invoke(java.lang.Object, java.lang.Object); Code: 0: aload_1 ; uložit první parametr metody na zásobník 1: aconst_null 2: astore_1 ; dosazení null do prvního parametru (leží v oblasti lokálních proměnných) 3: aload_2 ; uložit druhý parametr metody na zásobník 4: aconst_null 5: astore_2 ; dosazení null do druhého parametru (leží v oblasti lokálních proměnných) ; nyní jsou na zásobníku obě předané reference ; - můžeme zavolat metodu clojure.lang.Numbers.add(x y) 6: invokestatic #34; //Method clojure/lang/Numbers.add:(Ljava/lang/Objec t;Ljava/lang/Object;)Ljava/lang/Number; 9: areturn ; výsledek vrácený touto metodou je současně i návratovou hodnotou }
9. Odkazy na Internetu
- Eulerovo číslo
http://cs.wikipedia.org/wiki/Eulerovo_číslo - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Clojure.org: Clojure home page
http://clojure.org/downloads - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - A Couple of Clojure Agent Examples
http://lethain.com/a-couple-of-clojure-agent-examples/ - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html