Programovací jazyk Clojure 12: překlad programů z Clojure do bajtkódu JVM

Pavel Tišnovský 28. 8. 2012

Dnes se společně seznámíme se způsobem překladu aplikací napsaných v jazyku Clojure do bajtkódu JVM. V průběhu vývoje programů je sice většinou vhodné používat interaktivní interpret Clojure, ovšem výsledná aplikace se lépe dodává a nasazuje do produkce například ve formě Java archivu (JAR).

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í

6. Přístup k prvkům polí

7. Překlad funkcí Clojure do bajtkódu JVM

8. Obsah vygenerovaných souborů .class

9. Odkazy na Internetu

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):

widgety

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

  1. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  2. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  3. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  4. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  5. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  6. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  7. Clojure.org: Clojure home page
    http://clojure.org/downloads
  8. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  9. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  10. Clojure.org: Atoms
    http://clojure.org/Atoms
  11. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  12. A Couple of Clojure Agent Examples
    http://lethain.com/a-couple-of-clojure-agent-examples/
  13. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  14. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  15. 4Clojure
    http://www.4clojure.com/
  16. ClojureDoc
    http://clojuredocs.org/
  17. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  18. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  19. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  20. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  21. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  22. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  23. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  24. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  25. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  26. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  27. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  28. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  29. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  30. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  31. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  32. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  33. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  34. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  35. 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/
  36. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  37. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  38. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  39. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  40. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  41. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  42. BCEL Home page
    http://commons.apache.org/bcel/
  43. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  44. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  45. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  46. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  47. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  48. ASM Home page
    http://asm.ow2.org/
  49. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  50. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  51. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  52. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  53. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  54. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  55. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  56. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  57. Cobertura
    http://cobertura.sourceforge.net/
  58. FindBugs
    http://findbugs.sourceforge.net/
  59. GNU Classpath
    www.gnu.org/s/classpath/
  60. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  61. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  62. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  63. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  64. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  65. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  66. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  67. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  68. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  69. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  70. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  71. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  72. Scala Programming Language
    http://www.scala-lang.org/
  73. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  74. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  75. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  76. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  77. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  78. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  79. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  80. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  81. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  82. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  83. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  84. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  85. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  86. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  87. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  88. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  89. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html
Našli jste v článku chybu?
Vitalia.cz: Inspekce našla nelegální sklad v SAPĚ. Zase

Inspekce našla nelegální sklad v SAPĚ. Zase

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr

Vitalia.cz: Jaký je rozdíl mezi brambůrky a chipsy?

Jaký je rozdíl mezi brambůrky a chipsy?

DigiZone.cz: Test LG 55UH750V aneb Cena/výkon

Test LG 55UH750V aneb Cena/výkon

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

DigiZone.cz: Numan Two: rozhlasový přijímač s CD

Numan Two: rozhlasový přijímač s CD

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

Podnikatel.cz: EET pro e-shopy? Postavené na hlavu

EET pro e-shopy? Postavené na hlavu

Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Lupa.cz: Aukro.cz mění majitele. Vrací se do českých rukou

Aukro.cz mění majitele. Vrací se do českých rukou

120na80.cz: Hrbatá prsa aneb mýty o implantátech

Hrbatá prsa aneb mýty o implantátech

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

DigiZone.cz: Mordparta: trochu podchlazený 87. revír

Mordparta: trochu podchlazený 87. revír