Obsah
1. Volání funkcí a metod v bajtkódu Lua VM
2. Rozdíly mezi programovacími jazyky Java a Lua při volání funkcí/metod
3. Demonstrační příklad Test25.lua: volání funkcí v programovacím jazyku Lua
4. Překlad demonstračního příkladu Test25.lua do bajtkódu Lua VM
5. Globální proměnná _ENV a její použití v bajtkódu
6. Demonstrační příklad Test26.lua: výpis obsahu globální proměnné _ENV
7. Demonstrační příklad Test27.lua: výpis obsahu globální proměnné _ENV (úprava příkladu Test25.lua)
8. Demonstrační příklad Test28.lua: volání „statických metod“ v programovacím jazyku Lua
9. Překlad demonstračního příkladu Test28.lua do bajtkódu Lua VM
10. Demonstrační příklad Test29.lua: volání „nestatických metod“ v programovacím jazyku Lua
11. Překlad demonstračního příkladu Test29.lua do bajtkódu Lua VM
12. Překlad volání metod do bajtkódu
13. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
1. Volání funkcí a metod v bajtkódu Lua VM
V předchozí části seriálu o programovacím jazyku Java i o virtuálním stroji Javy jsme si na několika demonstračních příkladech ukázali, jakým způsobem je v bajtkódu JVM realizováno volání metod. Víme již, že je rozdíl mezi voláním statických metod, nestatických metod, privátních metod, konstruktorů i metod předepsaných v nějakém rozhraní (interface). Volání je realizováno přes instrukce INVOKESTATIC, INVOKEVIRTUAL, INVOKESPECIAL a INVOKEINTERFACE. Tyto instrukce navíc využívají takzvanou signaturu metody, což je jméno metody doplněné o typ její návratové hodnoty i o počet a typ všech parametrů metody (JVM, přesněji řečeno bajtkód JVM zde musí reflektovat fakt, že Java je silně a současně i staticky typovaný programovací jazyk). Dnes se budeme zabývat podobným tématem, ovšem z pohledu programovacího jazyka Lua a bajtkódu virtuálního stroje Lua VM. Uvidíme, že mezi JVM a Lua VM existují v této oblasti značné rozdíly.
2. Rozdíly mezi programovacími jazyky Java a Lua při volání funkcí/metod
Mezi programovacími jazyky Java a Lua existují velké rozdíly v tom, jakým způsobem je realizováno volání funkcí popř. metod. Většina těchto rozdílů vychází z faktu, že programovací jazyk Java je striktně a navíc ještě staticky typovaný, což se týká i parametrů metod, zatímco v případě programovacího jazyka Lua je typ parametrů vyhodnocován až v době běhu aplikace (při volání konkrétní funkce či metody). Navíc se v jazyku Lua nemusí při volání funkcí dodržet přesný počet parametrů – nadbytečné parametry nejsou využity a naopak, pokud se při volání uvede menší počet parametrů, než by odpovídalo počtu argumentů funkce, jsou tyto argumenty nastaveny na hodnotu nil. Aby byla situace ještě poněkud komplikovanější, je v Lua možné, aby funkce vracely více než jednu hodnotu a z funkcí lze i s (na)vázanými proměnnými vytvořit uzávěr (closure). Základní rozdíly mezi Javou a Luou jsou vypsány v tabulce pod tímto odstavcem:
# | Vlastnost | Java | Lua |
---|---|---|---|
1 | Podpora pro volání funkcí | Ne | funkce() |
2 | Podpora pro volání metod | Ano (viz níže) | Ano (viz níže) |
3 | Volání statických metod | Třída.funkce() | objekt.funkce() |
4 | Volání nestatických metod | objekt.funkce() | objekt:funkce() |
5 | Kontrola počtu parametrů při překladu | Ano | Ne |
6 | Podpora typu parametrů při překladu | Ano | Ne |
7 | Podpora proměnného počtu parametrů | Ano | Ano |
8 | Podpora proměnného počtu návratových hodnot | Ne | Ano |
9 | Funkce je plnohodnotný datový typ | Ne pro Javu<=7 | Ano |
10 | Podpora uzávěrů (closures) | Ne pro Javu<=7 | Ano |
3. Demonstrační příklad Test25.lua: volání funkcí v programovacím jazyce Lua
Dnešní první demonstrační příklad nazvaný Test25.lua je velmi jednoduchý. Jsou v něm implementovány funkce se jmény function1(), function2() a function3(), přičemž funkce function1() neočekává žádné argumenty, funkce function2() očekává jeden argument a konečně funkce function3() očekává dva argumenty, které jsou sečteny (v případě, že argumenty nejsou typu celé či reálné číslo, vznikne při běhu programu chyba). Všechny tři zmíněné funkce jsou volány z kódu, který je implementován v další trojici funkcí nazvaných callFunction1(), callFunction2() a callFunction3(). Povšimněte si, že se nikde nekontroluje ani počet ani typ skutečně předávaných parametrů:
-- -- Demonstracni priklad cislo 25. -- -- Volani funkci v programovacim jazyce Lua. -- -- -- Funkce bez parametru. -- function function1() end -- -- Funkce s jednim parametrem. -- function function2(x) return x end -- -- Funkce se dvema parametry. -- function function3(x, y) if x and y then return x+y else return nil end end -- -- Volani funkce function1(). -- function callFunction1() function1() function1(nil) function1(42) function1(1, 2) function1("xyzzy") end -- -- Volani funkce function2(). -- function callFunction2() function2() function2(nil) function2(42) function2(1, 2) function2("xyzzy") end -- -- Volani funkce function3(). -- function callFunction3() function3() function3(nil) function3(42) function3(1, 2) function3("xyzzy") end -- -- Spusteni testu. -- function main() callFunction1() callFunction2() callFunction3() end main() -- -- Finito. --
4. Překlad demonstračního příkladu Test25.lua do bajtkódu Lua VM
Funkce function1(), function2() a function3() se do bajtkódu Lua VM přeloží následujícím způsobem:
function1(): function <Test25.lua:12,13> (1 instruction at 0x8549c88) 0 params, 2 slots, 0 upvalues, 0 locals, 0 constants, 0 functions 1 [13] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (0) for 0x8549c88: locals (0) for 0x8549c88: upvalues (0) for 0x8549c88: function2(): function <Test25.lua:20,22> (2 instructions at 0x8549de0) 1 param, 2 slots, 0 upvalues, 1 local, 0 constants, 0 functions 1 [21] RETURN 0 2 ; navratova hodnota - konstanta ulozena na indexu 2 2 [22] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (0) for 0x8549de0: locals (1) for 0x8549de0: 0 x 1 3 ; parametr funkce zde vystupuje jako lokalni promenna upvalues (0) for 0x8549de0: function3(): function <Test25.lua:29,35> (10 instructions at 0x8549f58) 2 params, 3 slots, 0 upvalues, 2 locals, 0 constants, 0 functions 1 [30] TEST 0 0 ; test hodnoty prvniho parametru funkce 2 [30] JMP 0 5 ; kdyz x==nil, skok na instrukci cislo 8 3 [30] TEST 1 0 ; test hodnoty druheho parametru funkce 4 [30] JMP 0 3 ; kdyz y==nil, skok na instrukci cislo 8 5 [31] ADD 2 0 1 ; secist hodnoty obou parametru 6 [31] RETURN 2 2 ; a vratit vysledek souctu 7 [31] JMP 0 2 ; skok na instrukci cislo 10 8 [33] LOADNIL 2 0 ; cil obou podminenych skoku, bude se vracet nil 9 [33] RETURN 2 2 ; vyskok z funkce, vraci se hodnota nil 10 [35] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (0) for 0x8549f58: locals (2) for 0x8549f58: 0 x 1 11 ; prvni parametr funkce zde vystupuje jako lokalni promenna 1 y 1 11 ; druhy parametr funkce zde vystupuje jako lokalni promenna upvalues (0) for 0x8549f58:
Na způsobu překladu všech tří funkcí function1(), function2() a function3() není nic překvapivého, zajímavější ovšem bude zjistit, jakým způsobem se tyto funkce volají:
callFunction1(): function <Test25.lua:42,48> (16 instructions at 0x8549d88) 0 params, 3 slots, 1 upvalue, 0 locals, 6 constants, 0 functions 1 [43] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 2 [43] CALL 0 1 1 ; zavolani funkce function1() 3 [44] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 4 [44] LOADNIL 1 0 ; parametr funkce se preda v registru R1 5 [44] CALL 0 2 1 ; zavolani funkce function1() 6 [45] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 7 [45] LOADK 1 -2 ; parametr funkce se preda v registru R1 8 [45] CALL 0 2 1 ; zavolani funkce function1() 9 [46] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 10 [46] LOADK 1 -4 ; prvni parametr funkce 11 [46] LOADK 2 -5 ; druhy parametr funkce 12 [46] CALL 0 3 1 ; zavolani funkce function1() 13 [47] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 14 [47] LOADK 1 -6 ; prvni parametr funkce se preda v registru R1 15 [47] CALL 0 2 1 ; zavolani funkce function1() 16 [48] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (6) for 0x8549d88: 1 "function1" 2 42 3 "function3" 4 1 5 2 6 "xyzzy" locals (0) for 0x8549d88: upvalues (1) for 0x8549d88: 0 _ENV 0 0 callFunction2(): function <Test25.lua:55,61> (16 instructions at 0x854a410) 0 params, 3 slots, 1 upvalue, 0 locals, 6 constants, 0 functions 1 [56] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 2 [56] CALL 0 1 1 ; zavolani funkce function2() 3 [57] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 4 [57] LOADNIL 1 0 ; parametr funkce se preda v registru R1 5 [57] CALL 0 2 1 ; zavolani funkce function2() 6 [58] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 7 [58] LOADK 1 -2 ; parametr funkce se preda v registru R1 8 [58] CALL 0 2 1 ; zavolani funkce function2() 9 [59] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 10 [59] LOADK 1 -4 ; prvni parametr funkce 11 [59] LOADK 2 -5 ; druhy parametr funkce 12 [59] CALL 0 3 1 ; zavolani funkce function1() 13 [60] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 14 [60] LOADK 1 -6 ; prvni parametr funkce se preda v registru R1 15 [60] CALL 0 2 1 ; zavolani funkce function2() 16 [61] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (6) for 0x854a410: 1 "function2" 2 42 3 "function3" 4 1 5 2 6 "xyzzy" locals (0) for 0x854a410: upvalues (1) for 0x854a410: 0 _ENV 0 0 callFunction3(): function <Test25.lua:68,74> (16 instructions at 0x854a690) 0 params, 3 slots, 1 upvalue, 0 locals, 5 constants, 0 functions 1 [69] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 2 [69] CALL 0 1 1 ; zavolani funkce function3() 3 [70] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 4 [70] LOADNIL 1 0 ; parametr funkce se preda v registru R1 5 [70] CALL 0 2 1 ; zavolani funkce function3() 6 [71] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 7 [71] LOADK 1 -2 ; parametr funkce se preda v registru R1 8 [71] CALL 0 2 1 ; zavolani funkce function3() 9 [72] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 10 [72] LOADK 1 -3 ; prvni parametr funkce 11 [72] LOADK 2 -4 ; druhy parametr funkce 12 [72] CALL 0 3 1 ; zavolani funkce function1() 13 [73] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na funkci 14 [73] LOADK 1 -5 ; prvni parametr funkce se preda v registru R1 15 [73] CALL 0 2 1 ; zavolani funkce function3() 16 [74] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (5) for 0x854a690: 1 "function3" 2 42 3 1 4 2 5 "xyzzy" locals (0) for 0x854a690: upvalues (1) for 0x854a690: 0 _ENV 0 0
5. Globální proměnná _ENV a její použití v bajtkódu
Z předchozího výpisu bajtkódu je patrné, že volání všech tří funkcí se provádí naprosto stejným způsobem, bez ohledu na očekávaný počet parametrů. Hodnoty aktuálních parametrů se přenáší v pracovních registrech, ne tedy na zásobníku, jako je tomu v JVM. Samotné zavolání funkce zajišťuje dvojice instrukcí GETTABUP a CALL, přičemž instrukce CALL očekává v prvním uvedeném registru referenci na volanou funkci, která je získána přes instrukci GETTABUP z globální proměnné _ENV.
Právě lokální proměnná nazvaná _ENV má v bajtkódu Lua VM velký význam, protože jsou v ní uloženy mj. i všechny globální funkce. Připomeňme si, že v programovacím jazyku Lua jsou funkce plnohodnotným datovým typem, což mj. znamená, že funkce (=hodnoty datového typu function) lze přiřazovat do proměnných, předávat je jiným funkcím jako parametry, získávat funkce jako návratovou hodnotu jiných funkcí atd. Právě díky této vlastnosti funkcí (viz též http://lua-users.org/wiki/FunctionsTutorial) je konstrukce bajtkódu při volání funkcí tak jednoduchá a navíc univerzální (nemusí se zavádět takové monstrózní jazykové konstrukce jako jsou anonymní třídy v Javě jen proto, že je zapotřebí v určitém místě kódu předat odkaz/referenci na již existující funkci nebo na funkci vytvořenou přímo na místě, kde je její tělo využíváno).
6. Demonstrační příklad Test26.lua: výpis obsahu globální proměnné _ENV
Dnešní druhý demonstrační příklad nazvaný Test26.lua je velmi jednoduchý. Po svém spouštění vypíše na standardní výstup obsah globální proměnné _ENV popsané v předchozí kapitole. Žádná další činnost se neprovádí:
-- -- Demonstracni priklad cislo 26. -- -- Vypis obsahu lokalni promenne _ENV. -- -- -- Spusteni testu. -- function main() -- vypis obsahu celeho asociativniho pole _ENV for key,val in pairs(_ENV) do print(key,val) end end main() -- -- Finito. --
Podívejme se nyní na to, jaký obsah proměnná _ENV měla na testovacím počítači. Pro větší přehlednost byl výstup seřazen filtrem sort. Vyznačen je prvek main odpovídající stejnojmenné funkci implementované v demonstračním příkladu:
arg table: 0x9845970 assert function: 0x8061d80 bit32 table: 0x98430e8 collectgarbage function: 0x8061c80 coroutine table: 0x98437a0 debug table: 0x9844160 dofile function: 0x8061be0 error function: 0x8061b50 getmetatable function: 0x8061ae0 _G table: 0x9842308 io table: 0x9843b58 ipairs function: 0x8061710 loadfile function: 0x8061a40 load function: 0x8061820 loadstring function: 0x8061820 main function: 0x9845de0 math table: 0x9844f68 module function: 0x9843700 next function: 0x8061730 os table: 0x9843930 package table: 0x9842858 pairs function: 0x80616f0 pcall function: 0x8061580 print function: 0x8061420 rawequal function: 0x80613c0 rawget function: 0x80612f0 rawlen function: 0x8061350 rawset function: 0x8061280 require function: 0x9843740 select function: 0x8061140 setmetatable function: 0x8061070 string table: 0x9842d58 table table: 0x98439a8 tonumber function: 0x8060e70 tostring function: 0x8060e30 type function: 0x8060de0 unpack function: 0x80694a0 _VERSION Lua 5.2 xpcall function: 0x8060cf0
7. Demonstrační příklad Test27.lua: výpis obsahu globální proměnné _ENV (úprava příkladu Test25.lua)
Zkusme si nyní pro úplnost zkombinovat oba předchozí demonstrační příklady Test25.lua a Test26.lua, aby bylo možné se ujistit, že reference na volané funkce jsou skutečně uloženy v globální proměnné _ENV. Nový demonstrační příklad nazvaný Test27.lua obsahuje všech šest funkcí function1(), function2(), function3(), callFunction1(), callFunction2() a callFunction3(). Tyto funkce by měly být viditelné ve výpisu globální proměnné _ENV:
-- -- Demonstracni priklad cislo 27. -- -- Kombinace prikladu 25 a 26. -- -- -- Funkce bez parametru. -- function function1() end -- -- Funkce s jednim parametrem. -- function function2(x) return x end -- -- Funkce se dvema parametry. -- function function3(x, y) if x and y then return x+y else return nil end end -- -- Volani funkce function1(). -- function callFunction1() function1() function1(nil) function1(42) function3(1, 2) function1("xyzzy") end -- -- Volani funkce function2(). -- function callFunction2() function2() function2(nil) function2(42) function3(1, 2) function2("xyzzy") end -- -- Volani funkce function3(). -- function callFunction3() function3() function3(nil) function3(42) function3(1, 2) function3("xyzzy") end -- -- Spusteni testu. -- function main() -- vypis obsahu celeho asociativniho pole _ENV for key,val in pairs(_ENV) do print(key,val) end end main() -- -- Finito. --
Po spuštění tohoto demonstračního příkladu lze ze (seřazeného) výpisu obsahu globální proměnné _ENV vyčíst, že jsou zde skutečně uloženy jak všechny tři volané funkce, tak i volající funkce, funkce main atd. atd.:
arg table: 0x95ec970 assert function: 0x8061d80 bit32 table: 0x95ea0e8 callFunction1 function: 0x95edf70 callFunction2 function: 0x95edd68 callFunction3 function: 0x95ecde0 collectgarbage function: 0x8061c80 coroutine table: 0x95ea7a0 debug table: 0x95eb160 dofile function: 0x8061be0 error function: 0x8061b50 function1 function: 0x95ed7e0 function2 function: 0x95edc80 function3 function: 0x95edcb0 getmetatable function: 0x8061ae0 _G table: 0x95e9308 io table: 0x95eab58 ipairs function: 0x8061710 loadfile function: 0x8061a40 load function: 0x8061820 loadstring function: 0x8061820 main function: 0x95ed6e0 math table: 0x95ebf68 module function: 0x95ea700 next function: 0x8061730 os table: 0x95ea930 package table: 0x95e9858 pairs function: 0x80616f0 pcall function: 0x8061580 print function: 0x8061420 rawequal function: 0x80613c0 rawget function: 0x80612f0 rawlen function: 0x8061350 rawset function: 0x8061280 require function: 0x95ea740 select function: 0x8061140 setmetatable function: 0x8061070 string table: 0x95e9d58 table table: 0x95ea9a8 tonumber function: 0x8060e70 tostring function: 0x8060e30 type function: 0x8060de0 unpack function: 0x80694a0 _VERSION Lua 5.2 xpcall function: 0x8060cf0
8. Demonstrační příklad Test28.lua: volání „statických metod“ v programovacím jazyku Lua
Díky tomu, že funkce jsou v programovacím jazyku Lua plnohodnotným datovým typem, je možné odkazy/reference na funkce vkládat i do tabulek (což je jediný strukturovaný datový typ tohoto jazyka). Tímto způsobem je možné v Lue vytvořit objekty, přičemž funkce vložené do tabulek lze chápat jako obdobu „statických metod“. „Statické metody“ jsou běžné funkce, při jejichž deklaraci je mezi jménem tabulky a jménem funkce použita tečka, která je též použita při volání této funkce. Podívejme se na příklad Test28.lua, který je přímo odvozen od dnešního prvního příkladu Test25.lua až na ten rozdíl, že původní funkce function1(), function2() a function3() jsou uloženy do tabulky testClass:
-- -- Demonstracni priklad cislo 28. -- -- Volani "statickych metod" v programovacim jazyce Lua. -- testClass = {} -- -- Funkce/staticka metoda bez parametru. -- function testClass.function1() end -- -- Funkce/staticka metoda s jednim parametrem. -- function testClass.function2(x) return x end -- -- Funkce/staticka metoda se dvema parametry. -- function testClass.function3(x, y) if x and y then return x+y else return nil end end -- -- Volani funkci/statickych metod. -- function callFunction1() testClass.function1() testClass.function1(nil) testClass.function1(42) testClass.function1(1, 2) testClass.function1("xyzzy") end -- -- Volani funkce function2(). -- function callFunction2() testClass.function2() testClass.function2(nil) testClass.function2(42) testClass.function2(1, 2) testClass.function2("xyzzy") end -- -- Volani funkce function3(). -- function callFunction3() testClass.function3() testClass.function3(nil) testClass.function3(42) testClass.function3(1, 2) testClass.function3("xyzzy") end -- -- Spusteni testu. -- function main() callFunction1() callFunction2() callFunction3() end main() -- -- Finito. --
9. Překlad demonstračního příkladu Test28.lua do bajtkódu Lua VM
Zajímavé bude se podívat na způsob překladu demonstračního příkladu Test28.lua. Nejprve si opět uvedeme překlad všech tří volaných „statických metod“:
function1(): function <Test28.lua:14,15> (1 instruction at 0x87f2d00) 0 params, 2 slots, 0 upvalues, 0 locals, 0 constants, 0 functions 1 [15] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (0) for 0x87f2d00: locals (0) for 0x87f2d00: upvalues (0) for 0x87f2d00: function2(): function <Test28.lua:22,24> (2 instructions at 0x87f2e20) 1 param, 2 slots, 0 upvalues, 1 local, 0 constants, 0 functions 1 [23] RETURN 0 2 ; navratova hodnota - konstanta ulozena na indexu 2 2 [24] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (0) for 0x87f2e20: locals (1) for 0x87f2e20: 0 x 1 3 ; parametr funkce zde vystupuje jako lokalni promenna upvalues (0) for 0x87f2e20: function3(): function <Test28.lua:31,37> (10 instructions at 0x87f2ca8) 2 params, 3 slots, 0 upvalues, 2 locals, 0 constants, 0 functions 1 [32] TEST 0 0 ; test hodnoty prvniho parametru funkce 2 [32] JMP 0 5 ; kdyz x==nil, skok na instrukci cislo 8 3 [32] TEST 1 0 ; test hodnoty druheho parametru funkce 4 [32] JMP 0 3 ; kdyz y==nil, skok na instrukci cislo 8 5 [33] ADD 2 0 1 ; secist hodnoty obou parametru 6 [33] RETURN 2 2 ; a vratit vysledek souctu 7 [33] JMP 0 2 ; skok na instrukci cislo 10 8 [35] LOADNIL 2 0 ; cil obou podminenych skoku, bude se vracet nil 9 [35] RETURN 2 2 ; vyskok z funkce, vraci se hodnota nil 10 [37] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (0) for 0x87f2ca8: locals (2) for 0x87f2ca8: 0 x 1 11 ; prvni parametr funkce zde vystupuje jako lokalni promenna 1 y 1 11 ; druhy parametr funkce zde vystupuje jako lokalni promenna upvalues (0) for 0x87f2ca8:
Oproti prvnímu demonstračnímu příkladu zde nenastaly žádné změny, ovšem rozdílný je inicializační kód, který tabulku _ENV naplní:
1 [9] NEWTABLE 0 0 0 2 [9] SETTABUP 0 -1 0 ; _ENV "testClass" 3 [14] GETTABUP 0 0 -1 ; _ENV "testClass" 4 [15] CLOSURE 1 0 ; 0x87f2d00 5 [14] SETTABLE 0 -2 1 ; "function1" - 6 [22] GETTABUP 0 0 -1 ; _ENV "testClass" 7 [24] CLOSURE 1 1 ; 0x87f2e20 8 [22] SETTABLE 0 -3 1 ; "function2" - 9 [31] GETTABUP 0 0 -1 ; _ENV "testClass" 10 [37] CLOSURE 1 2 ; 0x87f2ca8 11 [31] SETTABLE 0 -4 1 ; "function3" - 12 [50] CLOSURE 0 3 ; 0x87f3398 13 [44] SETTABUP 0 -5 0 ; _ENV "callFunction1" 14 [63] CLOSURE 0 4 ; 0x87f3528 15 [57] SETTABUP 0 -6 0 ; _ENV "callFunction2" 16 [76] CLOSURE 0 5 ; 0x87f3238 17 [70] SETTABUP 0 -7 0 ; _ENV "callFunction3" 18 [87] CLOSURE 0 6 ; 0x87f3830 19 [83] SETTABUP 0 -8 0 ; _ENV "main" 20 [91] GETTABUP 0 0 -8 ; _ENV "main" 21 [91] CALL 0 1 1 22 [91] RETURN 0 1 constants (8) for 0x87f2b50: 1 "testClass" 2 "function1" 3 "function2" 4 "function3" 5 "callFunction1" 6 "callFunction2" 7 "callFunction3" 8 "main"
Nyní se podívejme na způsob volání všech tří testovacích statických metod:
function <Test28.lua:44,50> (21 instructions at 0x87f3398) 0 params, 3 slots, 1 upvalue, 0 locals, 7 constants, 0 functions 1 [45] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 2 [45] GETTABLE 0 0 -2 ; "function1" 3 [45] CALL 0 1 1 ; zavolani funkce function1() 4 [46] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 5 [46] GETTABLE 0 0 -2 ; "function1" 6 [46] LOADNIL 1 0 ; parametr funkce se preda v registru R1 7 [46] CALL 0 2 1 ; zavolani funkce function1() 8 [47] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 9 [47] GETTABLE 0 0 -2 ; "function1" 10 [47] LOADK 1 -3 ; 42 11 [47] CALL 0 2 1 ; zavolani funkce function1() 12 [48] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 13 [48] GETTABLE 0 0 -2 ; "function3" 14 [48] LOADK 1 -5 ; 1 15 [48] LOADK 2 -6 ; 2 16 [48] CALL 0 3 1 ; zavolani funkce function1() 17 [49] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 18 [49] GETTABLE 0 0 -2 ; "function1" 19 [49] LOADK 1 -7 ; "xyzzy" 20 [49] CALL 0 2 1 ; zavolani funkce function1() 21 [50] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (7) for 0x87f3398: 1 "testClass" 2 "function1" 3 42 4 "function3" 5 1 6 2 7 "xyzzy" locals (0) for 0x87f3398: upvalues (1) for 0x87f3398: 0 _ENV 0 0 function <Test28.lua:57,63> (21 instructions at 0x87f3528) 0 params, 3 slots, 1 upvalue, 0 locals, 7 constants, 0 functions 1 [58] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 2 [58] GETTABLE 0 0 -2 ; "function2" 3 [58] CALL 0 1 1 ; zavolani funkce function2() 4 [59] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 5 [59] GETTABLE 0 0 -2 ; "function2" 6 [59] LOADNIL 1 0 ; parametr funkce se preda v registru R1 7 [59] CALL 0 2 1 ; zavolani funkce function2() 8 [60] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 9 [60] GETTABLE 0 0 -2 ; "function2" 10 [60] LOADK 1 -3 ; 42 11 [60] CALL 0 2 1 ; zavolani funkce function2() 12 [61] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 13 [61] GETTABLE 0 0 -2 ; "function2" 14 [61] LOADK 1 -5 ; 1 15 [61] LOADK 2 -6 ; 2 16 [61] CALL 0 3 1 ; zavolani funkce function2() 17 [62] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 18 [62] GETTABLE 0 0 -2 ; "function2" 19 [62] LOADK 1 -7 ; "xyzzy" 20 [62] CALL 0 2 1 ; zavolani funkce function2() 21 [63] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (7) for 0x87f3528: 1 "testClass" 2 "function2" 3 42 4 "function3" 5 1 6 2 7 "xyzzy" locals (0) for 0x87f3528: upvalues (1) for 0x87f3528: 0 _ENV 0 0 function <Test28.lua:70,76> (21 instructions at 0x87f3238) 0 params, 3 slots, 1 upvalue, 0 locals, 6 constants, 0 functions 1 [71] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 2 [71] GETTABLE 0 0 -2 ; "function3" 3 [71] CALL 0 1 1 ; zavolani funkce function3() 4 [72] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 5 [72] GETTABLE 0 0 -2 ; "function3" 6 [72] LOADNIL 1 0 ; parametr funkce se preda v registru R1 7 [72] CALL 0 2 1 ; zavolani funkce function3() 8 [73] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 9 [73] GETTABLE 0 0 -2 ; "function3" 10 [73] LOADK 1 -3 ; 42 11 [73] CALL 0 2 1 ; zavolani funkce function3() 12 [74] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 13 [74] GETTABLE 0 0 -2 ; "function3" 14 [74] LOADK 1 -4 ; 1 15 [74] LOADK 2 -5 ; 2 16 [74] CALL 0 3 1 ; zavolani funkce function3() 17 [75] GETTABUP 0 0 -1 ; z globalni promenne _ENV ziskat referenci na testClass 18 [75] GETTABLE 0 0 -2 ; "function3" 19 [75] LOADK 1 -6 ; "xyzzy" 20 [75] CALL 0 2 1 ; zavolani funkce function3() 21 [76] RETURN 0 1 ; navrat z funkce (automaticky vkladana instrukce) constants (6) for 0x87f3238: 1 "testClass" 2 "function3" 3 42 4 1 5 2 6 "xyzzy" locals (0) for 0x87f3238: upvalues (1) for 0x87f3238: 0 _ENV 0 0
Vidíme, že volání „statických metod“ se skutečně liší, a to zejména kvůli nutnosti načítat odkaz/referenci na funkci nikoli přímo z tabulky _ENV, ale z tabulky testClass. To je vlastně jediný rozdíl oproti prvnímu demonstračnímu příkladu.
10. Demonstrační příklad Test29.lua: volání „nestatických metod“ v programovacím jazyku Lua
V programovacím jazyku Lua je možné funkce vkládat do tabulek i s využitím dvojtečky namísto tečky. Tento malý syntaktický rozdíl však vede k vytvoření sémanticky odlišného kódu – takto vytvořená funkce totiž bude mít navíc jeden parametr, v němž se předá odkaz na objekt, tedy hodnota, která se v jiných programovacích jazycích označuje klíčovým slovem self nebo this. Odlišné je pak i volání takových funkcí, které lze podle jejich vlastností již nazývat „metodami“:
-- -- Demonstracni priklad cislo 29. -- -- Volani "metod" v programovacim jazyce Lua. -- testClass = {} -- -- Funkce/metoda bez parametru. -- function testClass:function1() end -- -- Funkce/metoda s jednim parametrem. -- function testClass:function2(x) return x end -- -- Funkce/metoda se dvema parametry. -- function testClass:function3(x, y) if x and y then return x+y else return nil end end -- -- Volani funkce function1(). -- function callFunction1() testClass:function1() testClass:function1(nil) testClass:function1(42) testClass:function1(1, 2) testClass:function1("xyzzy") end -- -- Volani funkce function2(). -- function callFunction2() testClass:function2() testClass:function2(nil) testClass:function2(42) testClass:function2(1, 2) testClass:function2("xyzzy") end -- -- Volani funkce function3(). -- function callFunction3() testClass:function3() testClass:function3(nil) testClass:function3(42) testClass:function3(1, 2) testClass:function3("xyzzy") end -- -- Spusteni testu. -- function main() callFunction1() callFunction2() callFunction3() end main() -- -- Finito. --
11. Překlad demonstračního příkladu Test29.lua do bajtkódu Lua VM
Jak se výše uvedený příklad přeloží do bajtkódu?
function1(): function <Test29.lua:14,15> (1 instruction at 0x8cb2d00) 1 param, 2 slots, 0 upvalues, 1 local, 0 constants, 0 functions 1 [15] RETURN 0 1 constants (0) for 0x8cb2d00: locals (1) for 0x8cb2d00: 0 self 1 2 novy parametr! upvalues (0) for 0x8cb2d00: function2(): function <Test29.lua:22,24> (2 instructions at 0x8cb2ea8) 2 params, 2 slots, 0 upvalues, 2 locals, 0 constants, 0 functions 1 [23] RETURN 1 2 2 [24] RETURN 0 1 constants (0) for 0x8cb2ea8: locals (2) for 0x8cb2ea8: 0 self 1 3 novy parametr! 1 x 1 3 upvalues (0) for 0x8cb2ea8: function3(): function <Test29.lua:31,37> (10 instructions at 0x8cb2ca8) 3 params, 4 slots, 0 upvalues, 3 locals, 0 constants, 0 functions 1 [32] TEST 1 0 zde je zmena, posun indexu parametru! 2 [32] JMP 0 5 ; to 8 3 [32] TEST 2 0 zde je zmena, posun indexu parametru! 4 [32] JMP 0 3 ; to 8 5 [33] ADD 3 1 2 6 [33] RETURN 3 2 7 [33] JMP 0 2 ; to 10 8 [35] LOADNIL 3 0 9 [35] RETURN 3 2 10 [37] RETURN 0 1 constants (0) for 0x8cb2ca8: locals (3) for 0x8cb2ca8: 0 self 1 11 novy parametr! 1 x 1 11 2 y 1 11 upvalues (0) for 0x8cb2ca8:
Při zpracování parametrů jsou indexy oproti předchozímu příkladu o jedničku zvýšeny, protože prvním parametrem je self, což je ostatně patrné i ze struktury locals.
12. Překlad volání metod do bajtkódu
Volání metod vypadá v bajtkódu následovně:
function <Test29.lua:44,50> (21 instructions at 0x8cb3450) 0 params, 4 slots, 1 upvalue, 0 locals, 7 constants, 0 functions 1 [45] GETTABUP 0 0 -1 ; _ENV "testClass" 2 [45] SELF 0 0 -2 ; "function1" 3 [45] CALL 0 2 1 4 [46] GETTABUP 0 0 -1 ; _ENV "testClass" 5 [46] SELF 0 0 -2 ; "function1" 6 [46] LOADNIL 2 0 7 [46] CALL 0 3 1 8 [47] GETTABUP 0 0 -1 ; _ENV "testClass" 9 [47] SELF 0 0 -2 ; "function1" 10 [47] LOADK 2 -3 ; 42 11 [47] CALL 0 3 1 12 [48] GETTABUP 0 0 -1 ; _ENV "testClass" 13 [48] SELF 0 0 -4 ; "function3" 14 [48] LOADK 2 -5 ; 1 15 [48] LOADK 3 -6 ; 2 16 [48] CALL 0 4 1 17 [49] GETTABUP 0 0 -1 ; _ENV "testClass" 18 [49] SELF 0 0 -2 ; "function1" 19 [49] LOADK 2 -7 ; "xyzzy" 20 [49] CALL 0 3 1 21 [50] RETURN 0 1 constants (7) for 0x8cb3450: 1 "testClass" 2 "function1" 3 42 4 "function3" 5 1 6 2 7 "xyzzy" locals (0) for 0x8cb3450: upvalues (1) for 0x8cb3450: 0 _ENV 0 0 function <Test29.lua:57,63> (21 instructions at 0x8cb2df0) 0 params, 4 slots, 1 upvalue, 0 locals, 7 constants, 0 functions 1 [58] GETTABUP 0 0 -1 ; _ENV "testClass" 2 [58] SELF 0 0 -2 ; "function2" 3 [58] CALL 0 2 1 4 [59] GETTABUP 0 0 -1 ; _ENV "testClass" 5 [59] SELF 0 0 -2 ; "function2" 6 [59] LOADNIL 2 0 7 [59] CALL 0 3 1 8 [60] GETTABUP 0 0 -1 ; _ENV "testClass" 9 [60] SELF 0 0 -2 ; "function2" 10 [60] LOADK 2 -3 ; 42 11 [60] CALL 0 3 1 12 [61] GETTABUP 0 0 -1 ; _ENV "testClass" 13 [61] SELF 0 0 -4 ; "function3" 14 [61] LOADK 2 -5 ; 1 15 [61] LOADK 3 -6 ; 2 16 [61] CALL 0 4 1 17 [62] GETTABUP 0 0 -1 ; _ENV "testClass" 18 [62] SELF 0 0 -2 ; "function2" 19 [62] LOADK 2 -7 ; "xyzzy" 20 [62] CALL 0 3 1 21 [63] RETURN 0 1 constants (7) for 0x8cb2df0: 1 "testClass" 2 "function2" 3 42 4 "function3" 5 1 6 2 7 "xyzzy" locals (0) for 0x8cb2df0: upvalues (1) for 0x8cb2df0: 0 _ENV 0 0 function <Test29.lua:70,76> (21 instructions at 0x8cb2fd0) 0 params, 4 slots, 1 upvalue, 0 locals, 6 constants, 0 functions 1 [71] GETTABUP 0 0 -1 ; _ENV "testClass" 2 [71] SELF 0 0 -2 ; "function3" 3 [71] CALL 0 2 1 4 [72] GETTABUP 0 0 -1 ; _ENV "testClass" 5 [72] SELF 0 0 -2 ; "function3" 6 [72] LOADNIL 2 0 7 [72] CALL 0 3 1 8 [73] GETTABUP 0 0 -1 ; _ENV "testClass" 9 [73] SELF 0 0 -2 ; "function3" 10 [73] LOADK 2 -3 ; 42 11 [73] CALL 0 3 1 12 [74] GETTABUP 0 0 -1 ; _ENV "testClass" 13 [74] SELF 0 0 -2 ; "function3" 14 [74] LOADK 2 -4 ; 1 15 [74] LOADK 3 -5 ; 2 16 [74] CALL 0 4 1 17 [75] GETTABUP 0 0 -1 ; _ENV "testClass" 18 [75] SELF 0 0 -2 ; "function3" 19 [75] LOADK 2 -6 ; "xyzzy" 20 [75] CALL 0 3 1 21 [76] RETURN 0 1 constants (6) for 0x8cb2fd0: 1 "testClass" 2 "function3" 3 42 4 1 5 2 6 "xyzzy" locals (0) for 0x8cb2fd0: upvalues (1) for 0x8cb2fd0: 0 _ENV 0 0
Jako novinka se zde objevuje nová instrukce SELF, která má tři operandy A,B a C. Tato instrukce naplní obsah registru R(A+1) obsahem registru R(B) a následně provede druhou operaci R(A) := R(B)[RK©], tj. naplní obsah registru R(A) hodnotou přečtenou z tabulky, jejíž reference je uložena v registru R(B) a index v konstantě RK©. Tato instrukce se použije před voláním metody pro naplnění jejího prvního (skrytého) parametru.
Příště si kromě dalších informací přehledně ukážeme rozdíly mezi Lua VM a JVM při volání metod (bude se jednat o o shrnutí dnešního tématu).
13. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
Všech pět dnes popsaných a využitých demonstračních příkladů naprogramovaných v jazyku Lua bylo uloženo do Mercurial repositáře umístěného na adrese http://icedtea.classpath.org/people/ptisnovs/jvm-tools/. Odkazy na prozatím poslední verze těchto příkladů naleznete v tabulce pod tímto odstavcem:
14. Odkazy na Internetu
- Python Byte Code Instructions
https://docs.python.org/release/2.5.2/lib/bytecodes.html - For-each Loop in Java
http://www.leepoint.net/notes-java/flow/loops/foreach.html - Programming in Lua (first edition)
http://www.lua.org/pil/contents.html - Programming in Lua: Numeric for
http://www.lua.org/pil/4.3.4.html - Programming in Lua: break and return
http://www.lua.org/pil/4.4.html - Programming in Lua: Tables
http://www.lua.org/pil/2.5.html - Programming in Lua: Table Constructors
http://www.lua.org/pil/3.6.html - Programovací jazyk Lua
http://palmknihy.cz/web/kniha/programovaci-jazyk-lua-12651.htm - Lua: Tables Tutorial
http://lua-users.org/wiki/TablesTutorial - Python 2.x: funkce range()
https://docs.python.org/2/library/functions.html#range - Python 2.x: typ iterátor
https://docs.python.org/2/library/stdtypes.html#iterator-types - Lua: Control Structure Tutorial
http://lua-users.org/wiki/ControlStructureTutorial - Lua Types Tutorial
http://lua-users.org/wiki/LuaTypesTutorial - Goto Statement in Lua
http://lua-users.org/wiki/GotoStatement - Python break, continue and pass Statements
http://www.tutorialspoint.com/python/python_loop_control.htm - For Loop (Wikipedia)
http://en.wikipedia.org/wiki/For_loop - Heinz Rutishauser
http://en.wikipedia.org/wiki/Heinz_Rutishauser - Parrot
http://www.parrot.org/ - Parrot languages
http://www.parrot.org/languages - Parrot Primer
http://docs.parrot.org/parrot/latest/html/docs/intro.pod.html - Parrot Opcodes
http://docs.parrot.org/parrot/latest/html/ops.html - Parrot VM
http://en.wikibooks.org/wiki/Parrot_Virtual_Machine - Parrot Assembly Language
http://www.perl6.org/archive/pdd/pdd06_pasm.html - Parrot Reference: Chapter 11 – Perl 6 and Parrot Essentials
http://oreilly.com/perl/excerpts/perl-6-and-parrot-essentials/parrot-reference.html - Python Bytecode: Fun With Dis
http://akaptur.github.io/blog/2013/08/14/python-bytecode-fun-with-dis/ - Python's Innards: Hello, ceval.c!
http://tech.blog.aknin.name/category/my-projects/pythons-innards/ - Byterun
https://github.com/nedbat/byterun - Python Byte Code Instructions
http://document.ihg.uni-duisburg.de/Documentation/Python/lib/node56.html - Python Byte Code Instructions
https://docs.python.org/3.2/library/dis.html#python-bytecode-instructions - Lua 5.2 sources
http://www.lua.org/source/5.2/ - Lua 5.2 sources – lopcodes.h
http://www.lua.org/source/5.2/lopcodes.h.html - Lua 5.2 sources – lopcodes.c
http://www.lua.org/source/5.2/lopcodes.c.html - dis – Python module
https://docs.python.org/2/library/dis.html - Comparison of Python virtual machines
http://polishlinux.org/apps/cli/comparison-of-python-virtual-machines/ - O-code
http://en.wikipedia.org/wiki/O-code_machine - 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 - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - 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/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - 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 - 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/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html