Obsah
1. LuaJ – implementace jazyka Lua v Javě
2. Přednosti a zápory implementace LuaJ
3. Instalace LuaJ a spouštění skriptů
4. Testovací skript pro zjištění rozdílu mezi Lua a LuaJ v rychlosti interpretace skriptů
5. Časy běhu skriptu
6. Spolupráce s knihovnami jazyka Java
7. Demonstrační příklad – vytvoření okna pomocí knihovny Swing
8. Odkazy na Internetu
1. LuaJ – implementace jazyka Lua v Javě
V předchozích částech tohoto seriálu jsme se převážně zabývali popisem použití interpretru jazyka Lua, který je napsán v ANSI (ISO) C – ostatně i systém LÖVE je určen pro céčkovou variantu Luy. Jedná se o původní implementaci tohoto jazyka, která je přenositelná na velké množství platforem, od jednoduchých mikrořadičů až po výkonná PC. Kromě této implementace však existuje i poměrně zdařilá implementace jazyka Lua nazvaná příhodně LuaJ, protože je vytvořena v programovacím jazyku Java, přičemž výsledný interpret lze použít jak pro platformu J2SE/J2EE (desktopy, aplikační servery atd.), tak i J2ME (rozličná mobilní zařízení). Ve své podstatě se jedná o virtuální stroj (Lua Virtual Machine) běžící ve druhém virtuálním stroji (JVM – Java Virtual Machine). Použitím platformy Java je mj. umožněno, aby vytvářené skripty využívaly bez větších omezení všech knihoven poskytovaných běhovým prostředím Javy (JRE), podobně jako tomu je například v případě skriptovacího jazyka Scala či Jythonu (poměrně oblíbená implementace Pythonu pro JRE).
Dokonce je možné použít interpret jazyka Lua jako standardní skriptovací engine odpovídající JSR-233 (dynamické skriptování, viz odkazy) – právě tímto způsobem je v Javě 6 implementován JavaScript, jedná se o projekt Mozilla Rhino. Javovská verze jazyka Lua si přitom zachovává svoji relativně malou velikost – celý archiv obsahující jak interpret, tak i překladač, má necelých 160 kB (adresa, ze které se tento archiv dá stáhnout, je uvedená v poslední kapitole). Varianta pro platformu J2ME je dokonce ještě o 45 kB menší, protože neobsahuje všechny knihovny. V dalším textu se budu zabývat především variantou určenou pro platformu J2SE, která může využívat všechny knihovny, které běhové prostředí J2SE nabízí, například knihovny AWT a Swing pro tvorbu grafického uživatelského rozhraní, rozsáhlé knihovny pro práci se soubory a sítěmi (IO, NIO, …), JDBC (rozhraní k relačním databázím), RMI (volání metod vzdálených objektů), JNDI atd. Samozřejmě lze LuaJ použít i s dalšími knihovnami i celými frameworky, například přidat podporu pro skriptování v Lua do aplikačních serverů (JBoss, WebSphere) atd.
2. Přednosti a zápory implementace LuaJ
Spojení platformy Javy a jazyka Lua je v několika ohledech výhodné. Programátoři pracující v jazyce Lua tak získávají přístup k velkému množství různých knihoven, ať již těch standardních (některé jsme si vypsali v předchozím odstavci), tak i například knihoven vyvíjených v rámci Apache Software Foundation, což je jistě významné rozšíření možností tohoto jazyka, především v porovnání s minimalisticky pojatými standardními knihovnami (coroutine, debug, file, io, os, string, math, table a package). Na druhou stranu mohou z tohoto spojení profitovat i programátoři v Javě, protože dostávají do rukou další výkonný jazyk, který v mnoha ohledech ideálně doplňuje samotnou staticky typovanou Javu o dynamický skriptovací jazyk umožňující v mnoha případech rychlejší vývoj a testování – to je ostatně také důvod, proč se dnes pozornost vývojářů zaměřuje i na další dynamické jazyky určené pro platformu Javy, především na Scalu a Jython. Mezi nevýhody lze počítat především ne zcela dokonalou zpětnou kompatibilitu s původními knihovnami jazyka Lua, což může znamenat, že některé skripty je nutné upravit (nekompatibilita je z velké míry způsobena rozdílem mezi rozhraním operačního systému a rozhraním poskytovaným JRE).
3. Instalace LuaJ a spouštění skriptů
Instalace interpretru i překladače LuaJ je velmi jednoduchá a lze ji provést na každém počítači, který obsahuje JRE (Java Runtime Environment) alespoň verze 1.5. Po stažení archivu z adresy http://luaj.sourceforge.net/ je zapotřebí tento archiv rozbalit, například nástrojem unzip. V archivu se nachází zdrojové kódy celého interpretru i překladače, dále pak soubory s testy a nakonec také – což je z našeho pohledu nejdůležitější – dva archivy ve formátu JAR – luaj-j2se-verze.jar a luaj-j2me-verze.jar. Tyto dva archivy obsahují interpret a překladač jazyka Lua i všechny základní knihovny. Pro spuštění interpretru lze použít následující příkaz, ve kterém je zapotřebí nahradit číslo 0.96 aktuální verzí souboru:
java -cp luaj-j2se-0.96.jar lua
Pokud je nutné přímo spustit nějaký skript napsaný v jazyce Lua, předá se název skriptu jako první parametr interpretru:
java -cp luaj-j2se-0.96.jar lua fib.lua
Popř. je možné skript přeložit do bajtkódu jazyka Lua (soubory s bajtkódem ovšem nemusí být přenositelné na céčkovou verzi interpretru):
java -cp luaj-j2se-0.96.jar luac fib.lua
4. Testovací skript pro zjištění rozdílu mezi Lua a LuaJ v rychlosti interpretace skriptů
Pro alespoň rámcové zjištění rozdílů rychlosti interpretace Lua skriptů různými interpretry („céčková“ lua vs. „javovská“ luaj) posloužil následující program, který po svém spuštění vypíše na standardní výstup prvních 25 členů Fibonacciho posloupnosti i časy výpočtu jednotlivých členů. Pro každý člen posloupnosti se zvlášť volá rekurzivní funkce bez ohledu na předchozí výsledky (které by bylo samozřejmě možné využít), což je samozřejmě neefektivní, ovšem na druhou stranu se lépe měří časové rozdíly intepretace tohoto programu. V testovacím skriptu se mj. používá i metoda clock() z knihovny os. Tato knihovna je sice v „céčkové“ verzi interpretru přímo dostupná, ovšem při použití Javovské verze, tj. luaj, je nejdříve nutné tuto knihovnu explicitně načíst, o což se postará příkaz pcall(require, „org.luaj.lib.j2se.J2seOsLib“). Zjištění, zda je knihovna os dostupná je snadné – jedná se o globální asociativní pole, takže lze použít jednoduchý test, zda existuje globální proměnná os:
-- Výpočet Fibonacciho posloupnosti s využitím -- (neefektivního) rekurzivního algoritmu -- nutné pouze při volání skriptu z LuaJ, -- při použití "céčkové" Luy lze zakomentovat print('os lib loaded: ', os ~= nil) local lib = "org.luaj.lib.j2se.J2seOsLib" print('require "'..lib..'"', pcall(require, lib) ) print('os lib loaded: ', os ~= nil) -- funkce provádějící vlastní rekurzivní výpočet -- Fibonacciho posloupnosti function fib(n) if n < 2 then return n else return fib(n - 1) + fib(n - 2) end end -- funkce, která vypočte a vytiskne n-té číslo -- Fibonacciho posloupnosti -- spolu s měřením času výpočtu function test(n) local time1 = os.clock() -- volat stejnou funkci 100x, pro zlepšení -- přesnosti výpočtu času local value for i = 1, 100 do value = fib(n) end local time2 = os.clock() local deltaTime = time2 - time1 -- tisk výsledků výpočtu i měření času print(n, value, math.floor(1000.0*time1), math.floor(1000.0*time2), math.floor(1000.0*deltaTime)) end -- výpis hlavičky tabulky print("n", "value", "time1", "time2", "delta") for n = 1, 25 do test(n) end -- finito
5. Časy běhu skriptu
Časy výpočtu získané interpretací testovacího skriptu s využitím původní „céčkové“ varianty jazyka Lua, přeložené s využitím překladače GCC pomocí dodávaného souboru Makefile, jsou vypsány v následující tabulce. Poznamenejme, že v souboru Makefile je mj. použita volba -O2, tj. kód interpreteru je optimalizován, i když se nejedná o nejúčinnější metodu optimalizace:
n value time1 time2 delta 1 1 0 0 0 2 1 0 0 0 3 2 0 0 0 4 3 0 0 0 5 5 0 10 10 6 8 10 10 0 7 13 10 10 0 8 21 10 10 0 9 34 10 20 10 10 55 20 30 9 11 89 30 40 10 12 144 40 60 19 13 233 60 100 40 14 377 100 160 60 15 610 160 260 100 16 987 260 420 159 17 1597 420 670 250 18 2584 670 1091 420 19 4181 1091 1762 671 20 6765 1762 2864 1101 21 10946 2864 4656 1791 22 17711 4656 7520 2864 23 28657 7520 12157 4637 24 46368 12157 19598 7440 25 75025 19598 31625 12027
V další tabulce jsou vypsány časy interpretace skriptu pomocí Javovského interpreteru LuaJ, který byla spuštěn na platformě Java 6 v režimu client (volba -client). Časy běhu jsou cca 2,5× delší, než ve výše uvedeném případě:
n value time1 time2 delta 1 1 10 10 0 2 1 10 50 40 3 2 50 60 9 4 3 60 80 20 5 5 80 80 0 6 8 80 80 0 7 13 80 90 9 8 21 90 100 10 9 34 100 120 19 10 55 120 150 30 11 89 150 190 40 12 144 190 260 70 13 233 260 380 120 14 377 380 561 181 15 610 561 861 299 16 987 861 1342 481 17 1597 1342 2123 781 18 2584 2123 3375 1251 19 4181 3375 5398 2022 20 6765 5398 8662 3264 21 10946 8662 13950 5287 22 17711 13950 22512 8562 23 28657 22512 36352 13839 24 46368 36352 58855 22503 25 75025 58855 95247 36392
Pokud se při spuštění interpreteru LuaJ povolí režim server (volba -server), výpočet, tj. doba interpretace skriptu, se podle očekávání zrychlí, ovšem nedosahuje takové rychlosti, jako při použití céčkové varianty interpreteru:
n value time1 time2 delta 1 1 10 10 0 2 1 10 30 19 3 2 30 40 10 4 3 40 651 611 5 5 651 671 20 6 8 671 701 29 7 13 701 772 71 8 21 772 842 69 9 34 852 862 10 10 55 862 882 20 11 89 882 922 40 12 144 922 972 49 13 233 972 1062 90 14 377 1062 1202 139 15 610 1202 1433 231 16 987 1433 1803 369 17 1597 1803 2404 601 18 2584 2404 3365 961 19 4181 3365 4928 1562 20 6765 4928 7471 2543 21 10946 7481 11567 4086 22 17711 11567 18197 6629 23 28657 18197 28942 10745 24 46368 28942 46397 17455 25 75025 46407 74488 28081
Po vynesení do grafu můžeme vidět rozdíl v rychlosti interpretace mezi originálním interpretrem Lua a interpretrem LuaJ spuštěného jak v režimu client, tak i server. Důležité jsou samozřejmě pouze poměry mezi jednotlivými časy, nikoli jejich absolutní hodnota (ta se bude lišit v závislosti na použitém počítači). Samozřejmě, že pokud skript většinu svého času stráví prováděním vstupně/výstupních operací, komunikací po síti, práci s GUI či čtením dat z databáze, tak se rozdíly mezi jednotlivými implementacemi zmenšují.
6. Spolupráce s knihovnami jazyka Java
V předchozích kapitolách jsme si řekli, že jedna z největších předností integrace jazyka Lua s Javou spočívá v možnosti využití prakticky všech Javovských knihoven z Lua skriptů, tj. přímo ve skriptu je možné vytvořit instanci libovolné Javovské třídy, přistupovat k atributům i metodám této instance, registrovat callback funkce zavolané ve chvíli, kdy uvnitř JVM (Java Virtual Machine) vznikne nějaká událost atd. Většina této funkcionality je obsažena v knihovně luajava, která programátorům nabízí funkci luajava.newInstance() určenou vytvoření instance Java třídy zadané svým jménem s předáním parametrů do konstruktoru, funkci luajava.createProxy(), která zajistí vytvoření Javovského objektu, jenž může zachytit události vzniklé v JVM a zavolat Lua funkci zaregistrovanou k tomuto typu události aj. Funkce dostupné v knihovně luajava jsou založeny na Reflection API, což mj. znamená, že tato knihovna není plně funkční na platformě J2ME (mobilní zařízení) a vytvoření instancí Javovských tříd je poněkud složitější, což si ukážeme v navazující části tohoto seriálu.
7. Demonstrační příklad – vytvoření okna pomocí knihovny Swing
V dnešním demonstračním příkladu, jehož výpis je uveden pod tímto odstavcem, je ukázáno, jakým způsobem je možné vytvářet instance tříd z Javovských knihoven a jak je možné volat statické metody (tj. funkce) těchto tříd i (nestatické) metody objektů. Na tomto příkladu je pravděpodobně nejzajímavější právě způsob vytváření instance tříd pomocí funkce luajava.newInstance(), protože při volání této funkce je nutné uvést plné jméno třídy ve formě řetězce (o tom, která třída se skutečně použije, se rozhoduje až za běhu skriptu) a popř. i parametry předávané konstruktoru. Vzhledem k tomu, že Lua je dynamicky typovaný jazyk, neuvádí se samozřejmě typ proměnné, ve které je uložena instance třídy (Lua ani neobsahuje žádnou syntaktickou konstrukci pro určení typu proměnné). Volání metod instance třídy se provádí pomocí operátoru :, který, jak již víme z předchozích částí tohoto seriálu, představuje pouze syntaktický cukr k operátoru . – jediný rozdíl spočívá v tom, že se při použití operátoru : automaticky doplní před první argument volané funkce parametr s instancí třídy.
K atributům objektů se přistupuje podobným způsobem, ovšem v tomto případě se používá se operátor ., viz například řádek s kódem frame:getContentPane():add(pane, borderLayout.CENTER ). Zajímavé je také to, že je možné přes funkci luajava.createProxy() zaregistrovat callback funkci (napsanou v Lua skriptu) zavolanou v případě výskytu nějaké události. V níže uvedeném demonstračním příkladu se jedná o událost vzniklou při kliknutí myší do vytvořeného okna (callback funkce je zaregistrována pro celé okno – frame). Samotné zaregistrování události vzniklé v JVM (Java Virtual Machine) v tomto případě zajistí knihovna luajava, která automaticky vytvoří a přes příslušné API zaregistruje neviditelnou Javovskou třídu, jenž vzniklou událost zachytí a po zachycení události se zavolá funkce napsaná v Lua skriptu. Při běžném použití je tento způsob zpracování událostí pro programátory transparentní.
frame = luajava.newInstance( "javax.swing.JFrame", "Texts" ); pane = luajava.newInstance( "javax.swing.JPanel" ); borderFactory = luajava.bindClass( "javax.swing.BorderFactory" ) border = borderFactory:createEmptyBorder( 30, 30, 10, 30 ) pane:setBorder( border ) label = luajava.newInstance( "javax.swing.JLabel", "This is a Label" ); layout = luajava.newInstance( "java.awt.GridLayout", 2, 2 ); pane:setLayout( layout ) pane:add( label ) pane:setBounds( 20, 30, 10, 30 ) borderLayout = luajava.bindClass( "java.awt.BorderLayout" ) frame:getContentPane():add(pane, borderLayout.CENTER ) jframe = luajava.bindClass( "javax.swing.JFrame" ) frame:setDefaultCloseOperation(jframe.EXIT_ON_CLOSE) frame:pack() frame:setVisible(true) local listener = luajava.createProxy("java.awt.event.MouseListener", { mouseClicked = function(me) print("clicked!", me) end }) frame:addMouseListener(listener)
8. Odkazy na Internetu
- James Roseborough, Ian Farmer: Getting Started with LuaJ
dokument obsažený přímo v instalaci LuaJ - SourceForge Luaj Project Page
http://luaj.sourceforge.net/ - SourceForge Luaj Download Area
http://sourceforge.net/project/platformdownload.php?group_id=197627 - LuaForge Luaj Project Page
http://luaforge.net/projects/luaj/ - LuaForge Luaj Project Area
http://luaforge.net/frs/?group_id=457 - Lua home page
http://www.lua.org/ - Lua: vestavitelný minimalista
http://www.root.cz/clanky/lua-vestavitelny-minimalista/ - Lua
http://www.linuxexpres.cz/praxe/lua - Lua
http://cs.wikipedia.org/wiki/Lua - Lua (programming language)
http://en.wikipedia.org/wiki/Lua_(programming_language) - The Lua Programming Language
http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html - Lua Programming Gems
http://www.lua.org/gems/ - LuaForge
http://luaforge.net/ - Forge project tree
http://luaforge.net/softwaremap/trove_list.php - JSR 223: Scripting for the JavaTM Platform:
http://jcp.org/en/jsr/detail?id=223 - Apache Software Foundation:
http://www.apache.org