Hlavní navigace

Kooperace mezi jazykem Lua a nativním (céčkovým) kódem: knihovna FFI

Pavel Tišnovský

Zatímco v předchozí části seriálu o využití jazyka Lua v praxi jsme se zabývali převážně rozhraním mezi C a Luou nabízeným přímo standardním interpretrem jazyka Lua, dnes se zaměříme na popis způsobu použití knihovny FFI, s níž je možné pracovat v tom případě, kdy se namísto standardního interpretru použije LuaJIT.

Obsah

1. Zopakování z minula: volání nativních funkcí ze standardních knihoven

2. První demonstrační příklad

3. Druhý demonstrační příklad

4. Vytvoření vlastní nativní knihovny

5. Volání funkcí z uživatelské knihovny

6. Třetí demonstrační příklad

7. Předávání různých datových typů do nativních funkcí

8. Čtvrtý demonstrační příklad

9. Základy práce s řetězci

10. Pátý demonstrační příklad

11. Práce s poli

12. Šestý demonstrační příklad

13. Repositář s dnešními demonstračními příklady

14. Odkazy na Internetu

1. Zopakování z minula: volání nativních funkcí ze standardních knihoven

V předchozím článku jsme se zabývali způsobem komunikace mezi nativním kódem (typicky naprogramovaným v jazyku C) a skripty napsanými v programovacím jazyku Lua. Připomeňme si, že již ve standardním interpretru jazyka Lua existuje možnost volání céčkové funkce z Lua skriptu a naopak i funkce naprogramované v jazyku Lua z céčka, tj. z nativního kódu. Volání není přímé, protože se parametry a návratové hodnoty předávají explicitně přes zásobník ovládaný interpretrem a nikoli přes standardní (nativní) zásobníkový rámec. Taktéž je v některých případech nutné provádět konverzi dat. Na druhou stranu je však rozhraní C → Lua i Lua → C navrženo takovým způsobem, že je volání funkcí bezpečné, programátor nativní části aplikace může explicitně povolit jen potřebné moduly a navíc nemá skript naprogramovaný v jazyku Lua prakticky žádnou možnost, jak poškodit obsah nativního zásobníkového rámce a tím například způsobit chybu (či útok) typu buffer overflow.

Pokud nějaká aplikace namísto standardního interpretru jazyka Lua využívá možnosti nabízené projektem LuaJIT, volání nativních funkcí z Lua skriptů se mnohem zjednoduší, a to díky knihovně Lua FFI. Tato knihovna pro svou činnost pouze potřebuje znát hlavičky nativních funkcí, které zpracuje a na jejich základě vytvoří tabulku Lua funkcí, které je možné ihned volat z Lua skriptů. Nativní knihovny, které se mají volat, je nutné explicitně načíst (a zmíněná tabulka funkcí je vytvořena pro každou takto načtenou knihovnu). Výjimkou jsou základní céčkové knihovny, konkrétně libc, libm a na Linuxu i knihovna libdl, které jsou načteny automaticky, což je samozřejmě výhodné, neboť se tak zjednoduší tvorba skriptů. Na systému Windows (32bitovém!) jsou takto automaticky načteny knihovny kernel32.dll, user32.dll a gdi32.dll (popř. i msvcrt_verze.dll. Hlavičky funkcí je možné získat například z hlavičkových souborů (pokud se tedy nepoužívají pro tvorbu názvů složitější makra).

2. První demonstrační příklad

Demonstrační příklad nazvaný ffi_example1 jsme si již ukazovali minule, ovšem pro úplnost si ho znovu uvedeme, a to z toho důvodu, aby se ukázal rozdíl mezi voláním nativních funkcí ze standardní knihovny a voláním funkcí z knihovny uživatelské. Předpokládejme tedy, že potřebujeme volat céčkovou funkci nazvanou rand(), které se nepředávají žádné parametry. Nejprve je nutné explicitně knihovnu FFI načíst, což zajišťuje první řádek skriptu. Následně se musí zapsat hlavička nativní funkce či funkcí, které se budou z Lua skriptu volat (řádek začínající na ffi.cdef. Samotné volání nativní funkce je již jednoduché: ffi.C.rand(), výsledek (návratová hodnota) je uložena do lokální proměnné nazvané random (ffi.C je tabulkou funkcí ze standardních knihoven):

-- Prvni demonstracni priklad vyuzivajici knihovnu FFI
 
local ffi = require("ffi")
 
-- Definice ceckovske funkce ze standardni knihovny
 
ffi.cdef[[
int rand(void);
]]
 
for i=0,10 do
    -- Zavolani ceckovske funkce
    local random = ffi.C.rand()
    print(random)
end

Tento příklad je nutné spustit LuaJITem!. Příklad výstupu:

1804289383
846930886
1681692777
1714636915
1957747793
424238335
719885386
1649760492
596516649
1189641421
1025202362

3. Druhý demonstrační příklad

I tento demonstrační příklad byl ukázán minule, takže si ho popíšeme jen stručně. V tomto příkladu se volá funkce nazvaná atoi(), která je součástí standardní knihovny programovacího jazyka C. Funkce atoi() akceptuje (čti vyžaduje) parametr typu řetězec, což je v případě programovacího jazyka C datový typ const char *, tedy ukazatel na první znak řetězce (klíčové slovo const zde může pomáhat překladači při optimalizacích a navíc – což je důležitější – nám říká, že funkce řetězec nebude modifikovat). Při volání funkce atoi() ze skriptu napsaného v Lua se automaticky provede konverze mezi Lua stringem a céčkovým řetězcem:

-- Druhy demonstracni priklad vyuzivajici knihovnu FFI
 
local ffi = require("ffi")
 
-- Definice ceckovske funkce ze standardni knihovny
 
ffi.cdef[[
int atoi(const char *);
]]
 
-- Zavolani ceckovske funkce
local value = ffi.C.atoi("42")
 
print(value)

Spuštění příkladu:

luajit ffi_example2.lua
42

4. Vytvoření vlastní nativní knihovny

Jak je z předchozích dvou demonstračních příkladů patrné, je volání funkcí umístěných ve standardních knihovnách velmi snadné a provádí se přes dynamicky tvořenou tabulku ffi.C. Poněkud náročnější je situace ve chvíli, kdy programátor vytváří jak nativní část aplikace, tak i část skriptovanou v jazyce Lua. V tomto případě je totiž nutné z nativních funkcí psaných v céčku vytvořit knihovnu a tu explicitně načíst ve chvíli inicializace Lua skriptu. Podívejme se na jednoduchý příklad zdrojového kódu test.c psaného v jazyce C, který obsahuje jedinou funkci, kterou budeme chtít volat z Lua skriptů:

void test(void)
{
    puts("***TEST***");
}

Postupovat je možné následujícím způsobem. Nejdříve se musí provést překlad céčkového zdrojového kódu do nativního objektového kódu:

gcc -fPIC -c -o test.o test.c

Výsledkem je objektový soubor nazvaný test.o. Následně se vytvoří knihovna příkazem:

gcc -shared -Wl,-soname,libtest.so -o libtest.so test.o

Povšimněte si, že výsledná knihovna má prefix „lib“. To je důležité, neboť kdyby tento prefix nebyl použit a knihovna se jmenovala jen test.so, nastaly by problémy s jejím načítáním.

5. Volání funkcí z uživatelské knihovny

Nyní se podíváme na způsob volání funkcí (v našem případě jediné funkce) z právě přeložené uživatelské knihovny. Nejprve nastavíme proměnnou shellu LD_LIBRARY_PATH takovým způsobem, aby ukazovala i na aktuální adresář, v němž je nativní knihovna uložena:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

Úpravy v Lua skriptu jsou jednoduché. Nově vytvořenou knihovnu je nutné explicitně načíst funkcí ffi.load(). Povšimněte si, že se použije jméno test a nikoli libtest:

local lib = ffi.load("test")

Hlavičku funkce je nutné zaregistrovat (interně je to ovšem poměrně složité):

ffi.cdef[[
void test(void);
]]

A posléze je již možné nativní funkci zavolat:

lib.test()

Spuštění:

luajit test.lua
***TEST***

6. Třetí demonstrační příklad

Dnešní třetí demonstrační příklad vlastně shrnuje všechny kroky, které jsme si popsali ve druhé a ve třetí kapitole. Příklad se jmenuje ffi_example3 a všechny potřebné zdrojové soubory i skripty naleznete na adrese https://github.com/tisnik/luajit-examples/tree/master/ffi/ffi_example3.

Céčková část příkladu

#include <stdio.h>
 
void function1(void)
{
    puts("Function1!");
}

Skript pro překlad céčkové části do nativní knihovny

gcc -fPIC -c -o ffi_example3.o ffi_example3.c
gcc -shared -Wl,-soname,libffi_example3.so -o libffi_example3.so ffi_example3.o

Výpis symbolů z nativní knihovny

objdump -T libffi_example3.so |grep text
00000000000006d5 g    DF .text  0000000000000012  Base        function1

Lua skript

-- Treti demonstracni priklad vyuzivajici knihovnu FFI
 
local ffi = require("ffi")
 
local lib = ffi.load("ffi_example3")
 
-- Definice ceckovske funkce z uzivatelske knihovny
 
ffi.cdef[[
void function1(void);
]]
 
-- Zavolani ceckovske funkce
lib.function1()

Spuštění demonstračního příkladu

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
luajit ffi_example3.lua

7. Předávání různých datových typů do nativních funkcí

Do nativních funkcí je ve většině případů nutné předávat parametry, takže je potřebné zajistit správnou kooperaci mezi skriptem psaným v jazyce Lua (kde je množina primitivních datových typů relativně malá) a funkcemi psanými v céčku, které je naopak známé velkým množstvím primitivních datových typů a jejich variant (signed/unsigned). Nejprve se podívejme na použití celočíselných i reálných datových typů. Zde je situace relativně jednoduchá, neboť knihovna FFI automaticky provede všechny potřebné konverze. K tomu nám též napomáhá fakt, že již jméno volané céčkové funkce je jednoznačné, na rozdíl od (například) C++ s přetěžováním funkcí, přičemž skutečně volaná funkce se rozpozná až na základě počtu a typů parametrů. Tato složitost nám v případě programovacího jazyka C zcela odpadá.

8. Čtvrtý demonstrační příklad

Způsob předávání celočíselných i reálných hodnot nativním funkcím je ukázán v dnešním čtvrtém demonstračním příkladu. V céčkovém zdrojovém kódu můžeme vidět deklaraci pěti funkcí, přičemž jedna funkce neočekává žádné parametry, další funkce očekává celočíselný parametr, třetí funkce dva celočíselné parametry, funkce čtvrtá očekává reálnou hodnotu (resp. přesněji řečeno hodnotu reprezentovanou v systému plovoucí řádové čárky) a poslední funkce očekává znak, resp. jeho kód (předpokládejme pro jednoduchost použití ASCII). Volání všech těchto funkcí z Lua skriptu je velmi snadné, což je patrné z výpisu tohoto skriptu:

Céčková část příkladu

#include <stdio.h>
 
void function1(void)
{
    puts("Function1!");
}
 
void function2(int x)
{
    printf("Function2! %d\n", x);
}
 
void function3(int x, int y)
{
    printf("Function3! %d\n", x+y);
}
 
void function4(float x)
{
    printf("Function4! %7.5f\n", 1.f/x);
}
 
void function5(char c)
{
    printf("Function5! %c\n", c);
}

Skript pro překlad céčkové části do nativní knihovny

gcc -fPIC -c -o ffi_example4.o ffi_example4.c
gcc -shared -Wl,-soname,libffi_example4.so -o libffi_example4.so ffi_example4.o

Výpis symbolů z nativní knihovny

objdump -T libffi_example4.so |grep text
00000000000007fa g    DF .text  000000000000002b  Base        function3
00000000000007c5 g    DF .text  0000000000000012  Base        function1
0000000000000825 g    DF .text  0000000000000033  Base        function4
0000000000000858 g    DF .text  0000000000000026  Base        function5
00000000000007d7 g    DF .text  0000000000000023  Base        function2

Lua skript

-- Ctvrty demonstracni priklad vyuzivajici knihovnu FFI
 
local ffi = require("ffi")
 
local lib = ffi.load("ffi_example4")
 
-- Definice ceckovskych funkci z uzivatelske knihovny
 
ffi.cdef[[
void function1(void);
void function2(int);
void function3(int, int);
void function4(float);
void function5(char);
]]
 
-- Zavolani ceckovskych funkci
lib.function1()
lib.function2(42)
lib.function3(1,2)
lib.function4(3)
lib.function5(97)

Spuštění demonstračního příkladu

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
luajit ffi_example4.lua
Function1!
Function2! 42
Function3! 3
Function4! 0.33333
Function5! a

9. Základy práce s řetězci

Zatímco práce s celočíselnými i reálnými parametry je i přes rozdíly mezi jazykem C a Lua snadno pochopitelná, poněkud složitější je situace ve chvíli, kdy se má pracovat s řetězci. Pokud nativní céčková funkce akceptuje parametr typu const char *, je možné při volání této funkce použít řetězec v jazyce Lua, i když tento řetězec je interně zpracováván odlišným způsobem (zejména se o jeho dealokaci stará automatický správce paměti). Opačný způsob komunikace, tedy vrácení řetězce z céčkové funkce do skriptu psaného v jazyce Lua, je nepatrně obtížnější, a to z toho důvodu, že se musí provést explicitní konverze funkcí ffi.string(). Této funkci se předá řetězec vrácený z nativní funkce (ten je z pohledu Luy typu „cdata“) a výsledkem je běžný Lua řetězec.

10. Pátý demonstrační příklad

V dnešním pátém demonstračním příkladu jsou ukázány oba směry komunikace při předávání řetězců do nativní funkce i při vrácení řetězce z jiné nativní funkce. Funkce nazvaná function1 akceptuje parametr typu const char *, což knihovna FFI správně pochopí a dovolí nám do této funkce předat řetězec přímo z jazyka Lua. Naproti tomu funkce nazvaná function2 je bezparametrická, ale vrací konstantu typu const char*. Tato hodnota nám sama o sobě není v Lua skriptu příliš užitečná, takže se musí s využitím výše zmíněné funkce ffi.string() provést její převod na Lua string. Pro ukázání rozdílu mezi vrácenou hodnotou a plnohodnotným řetězcem jsou po spuštění tohoto demonstračního příkladu vypsány obě hodnoty.

Céčková část příkladu

#include <stdio.h>
 
const char *HELLO_MSG = "Hello World!";
 
void function1(const char *str)
{
    printf("Function1! %s\n", str);
}
 
const char* function2(void)
{
    return HELLO_MSG;
}

Skript pro překlad céčkové části do nativní knihovny

gcc -fPIC -c -o ffi_example5.o ffi_example5.c
gcc -shared -Wl,-soname,libffi_example5.so -o libffi_example5.so ffi_example5.o

Výpis symbolů z nativní knihovny

objdump -T libffi_example5.so |grep text
0000000000000755 g    DF .text  0000000000000026  Base        function1
000000000000077b g    DF .text  0000000000000010  Base        function2

Lua skript

-- Paty demonstracni priklad vyuzivajici knihovnu FFI
 
local ffi = require("ffi")
 
local lib = ffi.load("ffi_example5")
 
-- Definice ceckovskych funkci z uzivatelske knihovny
 
ffi.cdef[[
void function1(const char *);
const char* function2(void);
]]
 
-- Zavolani ceckovskych funkci
lib.function1("Hello world!")
 
local return_value = lib.function2()
print(return_value)
 
local str = ffi.string(return_value)
print(str)

Spuštění demonstračního příkladu

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
luajit ffi_example5.lua
Function1! Hello world!
cdata<const char *>: 0x7f3299d11795
Hello World!

11. Práce s poli

Podobně problematická činnost, jakou je předávání řetězců, může být práce s poli. Jak již víme z předchozích částí tohoto seriálu, programovací jazyk Lua vlastně pojem klasického pole nezná, neboť tabulky mají dvojí podobu (a dvě části): část indexovaná celými čísly implicitně od jedničky a část indexovaná prakticky libovolným klíčem (kromě hodnoty nil). Při tvorbě datové struktury kompatibilní s céčkovým polem nám může pomoci funkce ffi.new, v jejímž prvním parametru je možné zapsat typ pole a případně i počet jeho prvků a další (nepovinné) parametry pak představují hodnotu jednotlivých prvků tohoto pole. Podívejme se na jednoduchý příklad sloužící pro vytvoření céčkového pole s deseti inicializovanými prvky:

ffi.new("int[10]", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

popř. praktičtěji (převod z Lua tabulky na pole):

ffi.new("int[10]", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

Alternativně je možné počet prvků předat v prvním parametru a inicializační hodnoty tak o jedno místo posunout. Volání funkce ffi.new může vypadat i takto:

ffi.new("int[?]", 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Hodnoty prvků není zapotřebí vypisovat v případě, že má být pole automaticky vynulováno:

ffi.new("int[10]", {})

12. Šestý demonstrační příklad

Práce s poli je ukázána v šestém a současně i dnešním posledním demonstračním příkladu, v jehož céčkové části je deklarována funkce nazvaná modifyArray. Této funkci se předá pole prvků typu int a funkce pouze změní šestý prvek tohoto pole (prvky jsou v céčku indexovány od nuly, na rozdíl od jazyka Lua). Neprovádí se přitom žádná kontrola, zda má pole skutečně minimálně šest požadovaných prvků!. Pole předané do této funkce je v Lua skriptu vytvořeno příkazem ffi.new(„int[10]“, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) a po zavolání nativní funkce modifyArray se nové prvky pole vypíšou na standardní výstup s využitím klasické počítané programové smyčky typu for, tj. podobně, jakoby se jednalo o běžnou Lua tabulku:

Céčková část příkladu

#include <stdio.h>
 
void modifyArray(int *array)
{
    /* neprovadi se zadna kontrola, zda pole skutecne existuje! */
    array[5] = -1;
}

Skript pro překlad céčkové části do nativní knihovny

gcc -fPIC -c -o ffi_example6.o ffi_example6.c
gcc -shared -Wl,-soname,libffi_example6.so -o libffi_example6.so ffi_example6.o

Výpis symbolů z nativní knihovny

objdump -T libffi_example6.so |grep text
0000000000000695 g    DF .text  0000000000000018  Base        modifyArray

Lua skript

-- Sesty demonstracni priklad vyuzivajici knihovnu FFI
 
local ffi = require("ffi")
 
local lib = ffi.load("ffi_example6")
 
-- Definice ceckovske funkce z uzivatelske knihovny
 
ffi.cdef[[
void modifyArray(int *array);
]]
 
local array = ffi.new("int[10]", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
 
-- Zavolani ceckovske funkce
lib.modifyArray(array)
 
-- vypis prvku pole
for i = 0, 9 do
    print(i, array[i])
end

Spuštění demonstračního příkladu

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
luajit ffi_example6.lua
0       1
1       2
2       3
3       4
4       5
5       -1
6       7
7       8
8       9
9       10

13. Repositář s dnešními demonstračními příklady

Všech šest demonstračních příkladů, které jsme si dnes popsali, bylo uloženo do Git repositáře dostupného na adrese https://github.com/tisnik/luajit-examples. V tabulce zobrazené pod tímto odstavcem naleznete odkazy na adresáře obsahující jak zdrojové kódy příkladů, tak i pomocné skripty apod:

14. Odkazy na Internetu

  1. LuaJIT – Just in Time překladač pro programovací jazyk Lua
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/
  2. LuaJIT – Just in Time překladač pro programovací jazyk Lua (2)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/
  3. LuaJIT – Just in Time překladač pro programovací jazyk Lua (3)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-3/
  4. LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-4/
  5. LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-5-tabulky-a-pole/
  6. LuaJIT – Just in Time překladač pro programovací jazyk Lua (6 – překlad programových smyček do mezijazyka LuaJITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-6-preklad-programovych-smycek-do-mezijazyka-luajitu/
  7. LuaJIT – Just in Time překladač pro programovací jazyk Lua (7 – dokončení popisu mezijazyka LuaJITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-7-dokonceni-popisu-mezijazyka-luajitu/
  8. LuaJIT – Just in Time překladač pro programovací jazyk Lua (8 – základní vlastnosti trasovacího JITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-8-zakladni-vlastnosti-trasovaciho-jitu/
  9. LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-9-dalsi-vlastnosti-trasovaciho-jitu/
  10. LuaJIT – Just in Time překladač pro programovací jazyk Lua (10 – JIT překlad do nativního kódu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-10-jit-preklad-do-nativniho-kodu/
  11. LuaJIT – Just in Time překladač pro programovací jazyk Lua (11 – JIT překlad do nativního kódu procesorů s architekturami x86 a ARM)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-11-jit-preklad-do-nativniho-kodu-procesoru-s-architekturami-x86-a-arm/
  12. LuaJIT – Just in Time překladač pro programovací jazyk Lua (12 – překlad operací s reálnými čísly)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-12-preklad-operaci-s-realnymi-cisly/
  13. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  14. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  15. lua2js
    https://www.npmjs.com/package/lua2js
  16. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  17. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  18. Source-to-source compiler
    https://en.wikipedia.org/wiki/Source-to-source_compiler
  19. JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
    http://www.hanselman.com/blog/Ja­vaScriptIsAssemblyLanguage­ForTheWebSematicMarkupIsDe­adCleanVsMachinecodedHTML­.aspx
  20. JavaScript is Web Assembly Language and that's OK.
    http://www.hanselman.com/blog/Ja­vaScriptIsWebAssemblyLangu­ageAndThatsOK.aspx
  21. Dart
    https://www.dartlang.org/
  22. CoffeeScript
    http://coffeescript.org/
  23. TypeScript
    http://www.typescriptlang.org/
  24. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  25. Static single assignment form (SSA)
    http://en.wikipedia.org/wi­ki/Static_single_assignmen­t_form
  26. Wikipedia: Mezijazyk
    http://cs.wikipedia.org/wi­ki/Mezijazyk
  27. LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
  28. The LuaJIT Project
    http://luajit.org/index.html
  29. LuaJIT FAQ
    http://luajit.org/faq.html
  30. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  31. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  32. LuaJIT Wiki
    http://wiki.luajit.org/Home
  33. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  34. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  35. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  36. Tcl Plugin Version 3
    http://www.tcl.tk/software/plugin/
  37. JavaScript: The Web Assembly Language?
    http://www.informit.com/ar­ticles/article.aspx?p=1856657
  38. asm.js
    http://asmjs.org/
  39. List of languages that compile to JS
    https://github.com/jashke­nas/coffeescript/wiki/List-of-languages-that-compile-to-JS
  40. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  41. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  42. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  43. emscripten
    http://kripken.github.io/emscripten-site/
  44. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  45. Emscripten – Fastcomp na GitHubu
    https://github.com/kripken/emscripten-fastcomp
  46. Clang (pro Emscripten) na GitHubu
    https://github.com/kripken/emscripten-fastcomp-clang
  47. Why not use JavaScript?
    https://ckknight.github.i­o/gorillascript/
  48. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  49. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  50. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
Našli jste v článku chybu?
Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

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

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

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

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

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

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

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

Vitalia.cz: Cena stejného léku se liší i o tisíce

Cena stejného léku se liší i o tisíce

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

DigiZone.cz: Rádio Šlágr má licenci pro digi vysílání

Rádio Šlágr má licenci pro digi vysílání

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Když přijdete o oko, přijdete na rok o řidičák

Když přijdete o oko, přijdete na rok o řidičák

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

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

Mondelez stahuje rizikovou čokoládu Milka

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

Jmenuje se Janina a žije bez cukru

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

Jsou čajové sáčky toxické?

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA