API Reference
lua-sys gives you the Lua C API of LuaJIT. Use it to make guest lua_State
instances and to control them from the host program.
local lua = require("lua-sys")
The module has three members:
| Member | Function |
|---|---|
lua.new() | Makes a guest state. |
lua.raw | The lua-sys.raw module. It binds the complete Lua C API with FFI. |
lua.profiler | The lua-sys.profiler module. It samples guest states. |
The high-level API below is the approved method to control a guest state. Values cross between the host and the guest in two ways: by copy for primitives, or by reference for tables, functions, userdata and threads.
Quick index
| Object | Members |
|---|---|
lua | new() |
lua.State | load, eval, globals, table, setHook, jitOff, jitOn, jitFlush, memory, setMemoryLimit, setChainLimit, chainDepth, close, L |
lua.Chunk | eval, call, pcall, xpcall, setName, setMode, getMode, isBytecode |
| guest callable | fn(...), fn:pcall(...) |
lua.Table | get, set, field syntax, pairs, ipairs, type, value, free |
lua.Value | type, value, free |
lua.HookInfo | debug fields, thread, stack() |
lua.Frame | locals, getLocal, setLocal, upvalues, getUpvalue, setUpvalue, eval |
lua.profiler | start, stop, print |
lua.raw | The complete Lua C API with FFI. Refer to Naming. |
How values cross the boundary
The guest is an independent interpreter. It has its own heap, its own globals,
its own package.loaded and its own JIT engine. Nothing is shared with the
host. Each value that crosses is a copy or a reference.
Host → guest
| Host value | Result in the guest |
|---|---|
nil, boolean, number, string | A copy. A string can contain null bytes. |
Plain Lua table { ... } | A new guest table. The conversion is recursive. Refer to state:table(). |
lua.Table | The same guest table, as a reference. |
lua.Value | The same guest value, as a reference. |
| Host function | A guest C closure. Guest code can call it. Each crossing makes a new closure, so two uses of the same host function are not equal in the guest. |
| Guest callable of this state | The original guest function. |
| All other values | An error: cannot push value of type '<type>' onto guest stack. |
Guest → host
| Guest value | Result on the host |
|---|---|
nil, boolean, number, string | A plain host value. |
table | A lua.Table proxy. The proxy is a live view, not a copy. |
function | A guest callable, which is a plain host function. |
userdata, lightuserdata, thread | A lua.Value reference. |
Thus type(x) on the host is a reliable test. A guest table always arrives as
a host table, and this value is always a lua.Table.
Arguments of host callbacks
Guest code can call a host function. The bridge converts the arguments from guest values to host values. Only primitives and tables can cross in this direction. A function, a userdata or a thread causes this error:
bridge: cannot pass <type> across independent states
To send these values to the host, put them in a guest table and pass the table.
Return values of host callbacks
A host callback can return primitives only: nil, boolean, number and
string. The bridge copies each returned value into the guest. This error
occurs if the callback returns a table, a lua.Table, a function or another
compound value:
bridge: host callback returned a <type>; only primitives (nil, boolean, number, string) can be returned from host to guest
To give structured data to the guest, write the data into the guest. You can do one of these two steps:
- Set a guest global.
- Let the guest pass a table to the callback. The callback gets a live
lua.Tableview and can change the table.
state:globals().fill = function(t)
t.timeout = 5 -- t is the guest table from the guest
t.retries = 3
return true -- a primitive result is correct
end
state:eval("local cfg = {}; fill(cfg); return cfg.timeout") -- 5
lua.new() → lua.State
Makes a new guest lua_State. These libraries are open in the new state: the
base library, package, coroutine, table, string, math, io, os,
debug, bit and jit.
The modules ffi and string.buffer are not preloaded. However,
require("ffi") and require("string.buffer") operate in the guest.
The new state is fully independent of the host interpreter.
local state = lua.new()
print(state:eval("return _VERSION")) -- Lua 5.1
print(state:eval("return jit.version")) -- LuaJIT 2.1.x
state:close()
Close the state with state:close() when you do not need it
again.
state.L → lua_State*
The raw guest state pointer, as an FFI cdata value. Use it with
lua-sys.raw, with ffi.C calls, or to compare it with
info.thread in a hook. The value is nil after the state is closed.
state:close()
Closes the guest state. The operation releases the heap, the registry references and all host callbacks of the state. A second call is safe.
All lua.Table objects, lua.Value objects and guest callables of the state
become invalid. Their registry references stop with the state.
Note
Each operation on a closed state gives the error
state is closed. This rule coversstate:eval,state:load,state:globals,state:table, thelua.Chunkmethods, thelua.Tablemethods and calls to a guest callable. A call withfn:pcall()givesfalse, "state is closed". A stored frame gives neutral results, as it does after the hook returns. The process does not stop.
Close the state one time only, at the end of its life. Do not use the objects of a closed state.
state:setChainLimit(levels)
Limits the depth of a nested host to guest chain. The default is 200 levels,
and the value 0 removes the limit.
Each level of a chain, for example a host callback that calls a guest function that calls a host callback, uses C stack. A chain that is too deep stops the process, and a guest can build such a chain on purpose: write the guest handler with a tail call, and the guest Lua stack stays flat while the C stack grows.
state:setChainLimit(500)
local ok, err = pcall(handler, 5000)
-- ok == false, err contains "cross-state call chain reached its limit"
The error follows the usual rules. A guest function raises it on the host, and
fn:pcall() returns it. Inside the guest, a pcall around the call that
reaches the limit catches it. The depth is restored on each exit, so the state
keeps its full budget after the error.
state:chainDepth() → integer
The number of levels of the host to guest chain that are active now. The value
is 0 outside a chain, and 1 inside a single host callback called from the
guest. Use it for diagnostics.
state:memory() → integer
The number of bytes that the guest state holds now. The number comes from the allocator of the state, so it is exact. A fresh state starts at about 35 KB, because the standard libraries are open.
local state = lua.new()
print(state:memory()) -- about 35960
local big = state:eval([[return string.rep("x", 1000000)]])
print(state:memory()) -- about 1 MB more
big = nil
state:eval("collectgarbage('collect')")
print(state:memory()) -- lower again
The count follows the blocks that the state holds, not the live data. After a collection, the state can keep memory for later use, so the value does not always fall to the earlier figure.
state:setMemoryLimit(bytes)
Limits the guest state to bytes. An allocation that would pass the limit
fails, and the guest gets the error not enough memory. The value 0 removes
the limit, which is the default. The two limits of a state are separate, and a
limit does not stop state:close().
state:setMemoryLimit(state:memory() + 200000)
local ok, err = state:load([[return string.rep("y", 2000000)]]):pcall()
-- ok == false, err contains "not enough memory"
state:setMemoryLimit(0) -- no limit again
The guest can catch the error and continue with less memory, so treat the limit as a control on the size of the state, and combine it with a hook for the execution time.
Evaluating code
state:load(code [, chunkName]) → lua.Chunk
Wraps Lua source code in a builder. You can set up the builder before the code
runs. The operation does not compile the code. Therefore, a syntax error occurs
at the call that runs the chunk, not at state:load().
chunkName sets the chunk name for the debug information. Guest code sees this
name as debug.getinfo(1, "S").source. Use the prefix @ for a file path, for
example "@/path/to/file.lua".
The argument code can be source text or LuaJIT bytecode. A chunk accepts both
by default. Refer to chunk:setMode() to refuse
one format, and to
chunk:isBytecode() to examine the source.
state:eval(code [, chunkName]) → value
Compiles and runs code immediately. The method returns the first result. It
returns nil if the chunk has no result. This method is the same as
state:load(code, chunkName):eval().
The method first compiles the code as return <code>. If the result is a
syntax error, it compiles the original source. Thus a bare expression operates,
and a block of statements operates also:
local state = lua.new()
print(state:eval("1 + 2")) -- 3
print(state:eval("return 1 + 2")) -- 3
print(state:eval("local x = 5; return x * 2")) -- 10
print(state:eval("local x = 5")) -- nil
An error in the guest, and a syntax error, becomes a string error on the host.
Use pcall to catch it:
local ok, err = pcall(function() return state:eval("error('boom')") end)
-- ok == false, err == "boom"
lua.Chunk
The builder from state:load(). It holds the source code, an optional chunk
name, an optional mode and the owner state.
The chunk compiles one time, at its first run, and keeps the result. Later runs use the compiled function again, so a run costs about the same as a call to a guest function. A chunk that returns a function still creates a new closure at each run. A change of the name or of the mode compiles the chunk again.
A chunk accepts source text and LuaJIT bytecode by default. Use
chunk:setMode() to refuse one format, and
chunk:isBytecode() to examine the source before
the chunk runs.
chunk:eval(...) → value
Compiles and runs the chunk. The arguments become ... in the guest. The
method returns the first result, or nil if the chunk has no result. An error
in the guest becomes an error on the host.
local chunk = state:load("return ...")
print(chunk:eval("hello")) -- hello
print(chunk:eval("world")) -- world
chunk:call(...)
Compiles and runs the chunk. The arguments become ... in the guest. The
method discards all results. Use it for a script with side effects.
state:load("print('hello from guest')"):call()
state:load("_sum = select('#', ...)"):call(1, 2, 3)
print(state:globals()._sum) -- 3
chunk:pcall(...) → true, ... | false, err
The same as :eval(), but the method returns errors and does not raise them.
The result is true and all results after a success, or false, err after a
failure. The method also catches a syntax error, because the compilation occurs
inside the protected call.
local ok, a, b = state:load("return ... + 1, ... * 2"):pcall(10)
-- ok == true, a == 11, b == 20
local ok2 = state:load("local x = 1"):pcall()
-- ok2 == true, because a chunk without a result gives only true
local ok3, err3 = state:load("1 +"):pcall()
-- ok3 == false, err3 == "[string \"1 +\"]:1: unexpected symbol near '1'"
local ok4, err4 = state:load("error('boom')"):pcall()
-- ok4 == false, err4 == "boom"
chunk:xpcall(...) → true, ... | false, err
This method has the same result as :pcall(). In addition, the error string
contains a guest stack traceback after a failure. The guest debug.traceback
is the error handler for the call, so the traceback is complete before the
stack clears.
local ok, err = state:load("error('boom')"):xpcall()
-- ok == false
-- err == "boom\nstack traceback:\n\t..."
Use :xpcall() to report an error in guest code with context. Use :pcall()
when the message alone is sufficient.
chunk:setName(name) → lua.Chunk
Sets the chunk name for the debug information. The method returns the chunk, so
you can add more calls. A new name makes the next run compile again. This method is the same as the argument chunkName of
state:load().
local src = state:load("return debug.getinfo(1, 'S').source")
:setName("@myscript.lua")
:eval()
print(src) -- @myscript.lua
chunk:setMode(mode) → lua.Chunk
Sets the format that the chunk accepts. The method returns the chunk, so you can add more calls. A new mode makes the next run compile again.
| Mode | Meaning |
|---|---|
"text" | Source text only. The loader refuses bytecode. |
"bytecode" | LuaJIT bytecode only. The loader refuses source text. |
"both" | Source text or bytecode. This is the default. |
The Lua mode letters "t", "b" and "bt" are also correct, and "binary"
is an alias of "bytecode". The value is not case sensitive.
A mismatch shows at the call that runs the chunk. The error is
attempt to load chunk with wrong mode:
local bytecode = state:eval("return string.dump(function() return 7 end)")
state:load(bytecode):eval() -- 7, the mode is "both"
state:load(bytecode):setMode("text"):eval() -- raises "wrong mode"
state:load(bytecode):setMode("bytecode"):eval() -- 7
state:load("return 1"):setMode("bytecode"):eval() -- raises "wrong mode"
The method chunk:pcall() returns the error instead of raising it:
local ok, err = state:load(bytecode):setMode("text"):pcall()
-- ok == false, err == "attempt to load chunk with wrong mode"
An unknown mode gives setMode: unknown mode "<mode>" (expected "text", "bytecode" or "both"). A value that is not a string gives setMode: mode must be "text", "bytecode" or "both", got <type>.
chunk:getMode() → "text" | "bytecode" | "both"
The format that the chunk accepts. The value is "both" until you call
setMode().
print(state:load("return 1"):getMode()) -- both
print(state:load("return 1"):setMode("text"):getMode()) -- text
chunk:isBytecode() → boolean
true when the source of the chunk starts with the escape byte that marks a
precompiled chunk. Use this method to examine data from a source that you do
not trust, before the chunk runs.
LuaJIT loads bytecode with the signature \27LJ. Other binary data also starts
with the escape byte. The loader refuses such data with cannot load incompatible bytecode or cannot load malformed bytecode.
local chunk = state:load(source)
if chunk:isBytecode() then
error("bytecode is not permitted")
end
chunk:setMode("text"):call()
The mode does not change the result of isBytecode().
chunk(...)
The __call metamethod. A direct call to a chunk is the same as
chunk:eval(...).
print(state:load("return ... * 2")(21)) -- 42
Guest function callables
A guest function arrives on the host as an ordinary host function. This event
occurs when the function is a result of state:eval(), :load():eval(),
chunk:pcall(), Table:get() or an iteration with pairs(). The host
function calls back into the guest:
local add = state:eval("return function(a, b) return a + b end")
print(add(1, 2)) -- 3
local double = state:eval("return function(x) return x * 2 end")
state:globals().double = double -- and back into the guest
print(state:eval("return double(21)")) -- 42
Arguments and results obey the rules above. The bridge copies primitives, and it passes tables by reference. An error in the guest raises an error on the host.
A guest function has one callable for the life of its state. Two fetches of the
same guest function therefore give the same host function, and == is true. Two
proxies of one guest table are different objects. Compare values in the guest
if identity is important.
fn:pcall(...) → true, ... | false, err
Calls the guest function with protection. The result is true and all results,
or false, err when the guest function raises an error. The host gets no
error.
local fn = state:eval("return function(x) return x * 2, x + 1 end")
local ok, a, b = fn:pcall(21)
-- ok == true, a == 42, b == 22
local boom = state:eval("return function() error('kaboom') end")
local ok2, err = boom:pcall()
-- ok2 == false, err == "[string \"return function() error('kaboom') end\"]:1: kaboom"
A guest function without a result gives true only. The method comes from a
function metatable, so the field fn.pcall is also available.
Globals and tables
state:globals() → lua.Table
Returns a lua.Table proxy for the global environment of the guest (_G):
local g = state:globals()
g.myVar = 42
print(state:eval("return myVar")) -- 42
print(g.print == nil) -- false, because print is a callable
Each call gives a new proxy for the same guest table. Two proxies are never
==. Keep one proxy, or compare the guest tables in the guest.
state:table([init]) → lua.Table
Makes a new empty guest table. The argument init is optional and is a plain
host table. The new table is not registered. Assign it to a global, return it,
or pass it to guest code.
local t = state:table({
name = "alice",
pos = { x = 1, y = 2 },
greet = function(n) return "hi " .. n end,
})
print(t.name) -- alice
print(t.pos.x) -- 1
print(t:get("greet")("world")) -- hi world
The bridge converts the keys and the values of init with the normal host →
guest rules:
Entry in init | Result in the guest |
|---|---|
Key of type string, number or boolean | A copy. |
Value nil, boolean, number or string | A copy. |
| Value is a plain nested table | A new guest table. The conversion is recursive. |
Value is a lua.Table or a lua.Value | The guest value, as a reference. |
| Value is a host function | A host callback. |
state:table() gives these errors:
| Condition | Error |
|---|---|
init is not a table (nil is permitted) | state:table() init argument must be a table, got <type> |
| A key is not a string, a number or a boolean | state:table(): unsupported key type '<type>' |
| The table contains itself, directly or mutually | state:table(): cycle detected in init table |
The cycle test tells a back edge from a duplicate. One table as two sibling values gives two copies and no error. A table that contains itself gives an error. A circular structure is not possible across state boundaries, so the bridge rejects it.
Table:get(key) → value
Reads a key. Primitive values come back directly. A guest function comes back
as a callable, a nested table as a lua.Table proxy, and a userdata or a
thread as a lua.Value. A key that is absent gives nil.
The key can be a primitive or a guest value, such as a lua.Table, a callable
or a lua.Value.
Table:set(key, value)
Writes a key. The value can be a primitive, a host function, a guest callable,
a lua.Table or lua.Value reference, or a plain host table. A plain host
table becomes a new guest table. A key with the value nil is removed.
Table field access
A lua.Table proxy sends field reads to :get() and field writes to :set().
Thus tbl.key is the same as tbl:get("key"), and tbl.key = v is the same
as tbl:set("key", v):
local g = state:globals()
g.myVar = 42 -- g:set("myVar", 42)
g.config = { timeout = 5 } -- plain table becomes a guest table
print(g.myVar) -- 42
print(g.config.timeout) -- 5
A method name has priority over a guest key. The names get, set, pairs,
ipairs, type, value and free always give the method of the proxy. To
read a guest key with one of these names, use :get() with a string, for
example t:get("type").
Table:pairs() → iterator
Gives a stateless iterator for all key/value pairs of the guest table. It is
the same as pairs() on a plain table, and it uses the next function of the
guest:
for k, v in t:pairs() do
print(k, v)
end
Table:ipairs() → iterator
Gives a stateless iterator for the integer keys 1..n. The iteration stops at
the first nil. It is the same as ipairs():
for i, v in t:ipairs() do
print(i, v)
end
Warning
Use the methods of the proxy. Do not use the host operators
#t,pairs(t)andipairs(t). These operators act on the host wrapper table of the proxy and its internal fields_state,_refand_type. The result is incorrect for the guest table. For example,#tgives0, andpairs(t)iterates the internal fields. The functionnext(t)is also incorrect. Always uset:pairs()andt:ipairs(). Use the#operator in the guest when you need a length.
lua.Value
A reference to a guest value without its own host proxy: userdata,
lightuserdata and thread. lua.Table has the same methods.
local out = state:eval("return io.stdout")
print(type(out), out:type()) -- table, userdata
print(tostring(out)) -- lua.userdata
out:free() -- release the registry reference now
Value:type() → string
The name of the guest type: "nil", "boolean", "number", "string",
"table", "function", "userdata", "lightuserdata" or "thread". For a
lua.Table, the result is "table".
Value:value() → any
The value behind the reference. For a live reference, the method returns the
object itself. The guest value is not copied. After free() or
state:close(), the method returns the stored plain value. That value is nil
for a referenced type.
Value:free()
Releases the guest registry reference immediately. The garbage collector also
releases the reference at collection of the proxy (__gc). Use free() to
reclaim memory early. A program that uses many guest values for a long time
benefits from this call.
free() is safe to call more than one time. It is also safe after
state:close().
Debug hooks and frames
state:setHook(fn, mask [, count])
Installs a debug hook on the guest state. This method is the high-level
equivalent of the raw lua_sethook. It needs no FFI cast and no raw callback.
The bridge calls fn as fn(event, info) at each event:
eventis"call","return","line","count"or"tailcall".infois alua.HookInfotable for the event.
mask selects the events. Use a string with event names and spaces, or an
integer bitmask:
| Name | Bit | Constant |
|---|---|---|
call | 1 | LUA_MASKCALL |
return (or ret) | 2 | LUA_MASKRET |
line | 4 | LUA_MASKLINE |
count | 8 | LUA_MASKCOUNT |
state:setHook(function(event, info) end, "line")
state:setHook(function(event, info) end, "call return")
state:setHook(function(event, info) end, 4) -- the same as "line"
state:setHook(function(event, info) end, "count", 1000)
count is the instruction interval for the "count" event. The default value
is 1. A new hook replaces the old hook. The call state:setHook(nil) removes
the hook.
An incorrect argument gives one of these errors:
| Condition | Error |
|---|---|
fn is not a function and not nil | setHook: fn must be a function or nil, got <type> |
mask is not a string and not a number | setHook: mask must be a string like "line" or an integer bitmask, got <type> |
mask contains an unknown word | setHook: unknown hook event '<word>' (expected call, return, line, count) |
mask is an empty string | setHook: hook mask cannot be empty |
The hook is on the guest state, not on the host. It operates while guest code runs, including code in a guest coroutine.
Note
LuaJIT fires hooks on interpreted code only. Thus the guest JIT engine is off while a hook is installed, and the bridge flushes the traces. Removal of the hook starts the engine again. A hook decreases the speed of hot guest code.
A hook that calls error() stops the running guest code with that error. A
pcall around the call that started the guest code catches the error, and
chunk:pcall also catches it. Therefore, a count hook is a simple timeout
check:
state:setHook(function()
error("timeout: guest code ran too long")
end, "count", 1000)
local ok, err = pcall(function()
state:eval("while true do end")
end)
-- ok == false, err contains "timeout"
lua.HookInfo
The info table of the hook callback. The bridge fills all debug fields at the
event, so the fields stay readable after the callback returns.
| Field | Type | Description |
|---|---|---|
event | string | "call", "return", "line", "count" or "tailcall". |
thread | lightuserdata | The lua_State* of the event. This is the main thread of the guest or a coroutine thread. |
name | string? | The function name, when available. |
namewhat | string? | The source of name, such as "global", "local" or "method". |
what | string? | "Lua", "main" or "C". |
source | string? | The source name of the chunk. |
short_src | string | The short source name. |
currentline | integer | The line at the hook point. The value is -1 if the line is not known. |
linedefined | integer | The first line of the running function. |
lastlinedefined | integer | The last line of the running function. |
nups | integer | The number of upvalues of the running function. |
The metatable of the table supplies the method info.stack. It is not a stored
field. Refer to info:stack().
info.thread is a lightuserdata with the thread pointer. Cast it to examine
that thread with the raw API. This method is necessary for the frames of a
coroutine, because lua_getstack requires the running thread:
local ffi = require("ffi")
state:setHook(function(event, info)
if ffi.cast("lua_State*", info.thread) ~= state.L then
-- the hook fired in a coroutine
end
end, "line")
info:stack() → lua.Frame[]
Returns the stack trace of the thread as an array of lua.Frame
objects. Item 1 is level 0, which is the frame of the hook event. The last
item is the outermost frame.
state:setHook(function(event, info)
for i, frame in ipairs(info:stack()) do
print(i, frame.what, frame.source, frame.currentline)
end
end, "line")
Warning
info:stack()examines the thread while it is paused at the hook. Call it inside the callback only. A later call raisesinfo:stack() must be called from within the hook callback. The debug fields stay readable after the callback. The frames become inactive as described inlua.Frame.
lua.Frame
One stack frame. A frame is valid while the thread is paused at the hook. A
frame has the same debug fields as info, but no event field. It has these
two more fields:
| Field | Type | Description |
|---|---|---|
thread | lightuserdata | The lua_State* of this frame. |
level | integer | The stack level. Level 0 is the frame of the hook event. |
A frame also has methods to read and write its state. A local and an upvalue
are copies that the bridge converts with the normal guest → host rules. Thus a
table local arrives as a lua.Table view of the live guest table.
Frame:locals() → { name, value }[]
Lists the active locals of the frame, in order. The result is an empty table if the frame does not exist, for example after the hook returns.
Frame:getLocal(name) → value
Reads one local by name. The result is nil if the frame has no local with
that name.
Frame:setLocal(name, value) → boolean
Writes to an active local. The result is true on a success, and false if
the local does not exist or the frame is gone. You can write only to locals
that are active at the hook point. The running program sees the change:
state:setHook(function(event, info)
local frame = info:stack()[1]
if frame:getLocal("marker") then
frame:setLocal("marker", 777) -- the guest reads 777 afterwards
end
end, "line")
Frame:upvalues() → { name, value }[]
Lists the upvalues of the running function as { name, value } pairs.
Frame:getUpvalue(name) → value
Reads one upvalue of the running function. The result is nil if the function
has no upvalue with that name.
Frame:setUpvalue(name, value) → boolean
Writes to an upvalue of the running function. The result shows if the bridge found the upvalue. A write to an upvalue changes the shared cell, so other closures over that cell see the new value.
Frame:eval(code) → true, any | false, err
Evaluates code with the locals and the upvalues of the frame in scope. The
code runs in a new environment that contains the active locals and the
upvalues. A read of another name continues to the environment of the running
function. A write to a new name goes there also. After a success, the bridge
writes assignments to existing locals and upvalues back into the frame. Thus
frame:eval("x = 42") changes the running program:
state:setHook(function(event, info)
local frame = info:stack()[1]
if frame:getLocal("marker") then
local ok, value = frame:eval("marker * 2") -- reads the local
frame:eval("marker = marker + 1") -- writes it back
end
end, "line")
The result is true, firstResult, or false, err after an error in the guest
or a syntax error. Like the other frame methods, call it inside the hook
callback only.
Frames after the hook returns
A frame is valid only while the thread is paused at the hook. After the hook returns, and after the close of its state, the methods give neutral results and do not stop the process:
| Call | Result |
|---|---|
frame:locals() | {} |
frame:getLocal(name) | nil |
frame:setLocal(name, value) | false |
frame:upvalues() | {} |
frame:getUpvalue(name) | nil |
frame:setUpvalue(name, value) | false |
frame:eval(code) | false, "no frame at stack level <level>" |
JIT control
state:jitOff([fn]) → state, state:jitOn([fn]) → state, state:jitFlush()
These three methods control the JIT compiler of the guest. Without an
argument, the method switches the complete engine of the state. With a guest
callable of the same state, the method changes that function only. jitOff and
jitOn return the state, so you can add more calls. jitFlush returns
nothing.
state:jitOff() -- disable the JIT for the complete guest state
state:jitOn() -- enable it again
state:jitOff(fn) -- disable compilation of one guest function
state:jitOn(fn) -- enable that function again
state:jitFlush() -- discard all compiled traces
An argument that is not a guest callable of this state gives
jitOff: fn must be a guest callable obtained from this state. The method
jitOn gives the same error. After close(), each of the three
methods gives state is closed.
state:setHook disables the engine while a hook is installed and flushes the
traces at removal. These methods give direct control. For example, you can keep
the other functions of the state compiled while one function stays
interpreted.
Profiler
local profiler = require("lua-sys.profiler") -- also lua.profiler
A sampling profiler for guest states. It uses the profiler hooks of LuaJIT.
profiler.start(state [, mode] [, callback])
Starts the sampling of state, which must be an open lua.State. The method
gives profiler.start: expected an open lua.State for nil, a plain table or
an already closed state. It gives
profiler already running for this state if the state is in the sampling mode
already.
The profiler keys its bookkeeping by the state object and not by its address.
A state that you close without profiler.stop therefore does not block the
next state.
modeis a LuaJIT profiler mode string. The default is"fi1". Useffor function-level stacks,lfor line-level stacks, andi<ms>for the sampling interval in milliseconds.callbackis optional and has the formfunction(stack, samples, vmstate). The bridge calls it at each sample.stackis the frame list with semicolons,samplesis the number of samples in this interval, andvmstateis a one-character string for the VM state from LuaJIT, for example"N"or"I". A callback stops the aggregation of the samples, sostopreturnsnil.
profiler.stop(state) → report
Stops the sampling. The method returns the aggregated report, in order of
sample count from high to low. The result is nil if the profiler started with
a custom callback. A state without active sampling gives
profiler not running for this state.
The report is an array of entries with an extra field total:
{
{ stack = "fib;fib;fib;main", vmstate = "N", count = 150, percent = 75.0 },
{ stack = "main", vmstate = "I", count = 50, percent = 25.0 },
total = 200,
}
| Field | Description |
|---|---|
stack | The frame names with semicolons. The innermost frame is first. |
vmstate | The one-character VM state of the samples in this entry. |
count | The number of samples in this entry. |
percent | The part of total, so the percentages add up to approximately 100. |
total | The complete number of samples. This field is on the report, not on an entry. |
profiler.print(report [, out] [, min_percent])
Writes the report to out as a table. The default for out is io.stdout.
The method hides entries below min_percent. The default is 1.
local state = lua.new()
local work = state:load("local function fib(n) if n < 2 then return n end return fib(n-1) + fib(n-2) end for i = 1, 300 do fib(20) end")
profiler.start(state)
work()
profiler.print(profiler.stop(state))
state:close()
lua-sys.raw
require("lua-sys.raw") is a thin FFI binding of the complete Lua 5.1 and
LuaJIT C API. The module is also available as lua.raw. Each function takes a
lua_State* as the first argument. Use state.L for a guest, or another state
pointer. The high-level API uses this module.
local raw = require("lua-sys.raw")
local L = raw.lnewstate()
raw.openlibs(L)
raw.loadstring(L, "return 2 + 2")
raw.pcall(L, 0, 1, 0) -- 0 == LUA_OK
print(raw.tonumber(L, -1)) -- 4
raw.close(L)
Naming
| C name | raw name | Example |
|---|---|---|
lua_X | raw.X | lua_gettop → raw.gettop |
luaL_X | raw.X if the name is free | luaL_loadstring → raw.loadstring, luaL_ref → raw.ref |
luaL_X | raw.lX if raw.X is in use | luaL_newstate → raw.lnewstate, because lua_newstate → raw.newstate |
luaopen_X | raw.openX | luaopen_string → raw.openString |
luaJIT_X | raw.jit_X | luaJIT_setmode → raw.jit_setmode |
More examples of the raw.lX form: luaL_error → raw.lerror,
luaL_checkstack → raw.lcheckstack, luaL_setmetatable →
raw.lsetmetatable.
Differences from the C API
- The C API gives an integer for a status or a test. In
raw, these results are Lua booleans:raw.equal,raw.rawequal,raw.lessthan,raw.next,raw.checkstack,raw.isnumber,raw.isstring,raw.iscfunction,raw.isuserdata,raw.isyieldable,raw.getmetatable,raw.setmetatable,raw.getfenv,raw.setfenv,raw.pushthread,raw.callmetaandraw.testudata. - The module wraps the output parameters.
raw.tolstring(L, idx)andraw.checklstring(L, idx)return a Lua string.raw.jit_profile_dumpstack(L, fmt, depth)copies the profiler buffer into a Lua string. The C buffer is valid until the next call only. - The module has helpers for common sequences:
raw.pop(L, n)islua_settop(L, -n-1), andraw.getglobal(L, name)islua_getfield(L, LUA_GLOBALSINDEX, name). state.Lis alua_State*cdata. Eachrawfunction accepts it directly.
Caution
A
rawcall into a state uses the LuaJIT FFI. The FFI is not safe for re-entry across independent states. A guest state that you control withrawwhile guest code calls back into the host can stop the trace recorder of LuaJIT. Use the high-level API for all traffic between the host and a guest. The bridge sends each transition through alua_CFunction, which the JIT treats as an opaque boundary. Refer to Bridge Design for the full explanation.
lua-sys.bridge
An internal compiled module (bridge.so, .dylib or .dll). Its functions
support the high-level API: new_state, close_state, make_callable,
push_callback, register, unregister, set_hook, remove_hook,
compound_tag and set_frame_meta.
These functions are not a public interface. Their signatures and their
operation can change without notice. Do not call them. Use lua-sys.raw when
you need a function below the high-level API.
Rules and caveats
- Close the state one time, at the end.
state:close()releases everything that belongs to the state, including the references in it. The guarded methods givestate is closed. All other operations, such as the evaluation of code, table access and calls to guest functions, stop the process when the state is gone. - References, not copies. A
lua.Table, alua.Valueand a callable point to values in the registry of the guest state. They are valid while the state is valid. The garbage collector releases them. - A state is fully isolated. The guest cannot see host globals or host
modules. The host cannot see guest globals, except through the API. Two guest
states never share values. A value of one state cannot go into the other
state, and the bridge gives
cannot pass <type> across independent states. - Some results are discarded by design.
state:evalandchunk:evalreturn the first result.chunk:callreturns no result. Usechunk:pcallorfn:pcallto get all results. - Errors are strings. An error in the guest becomes a plain string error on
the host. The string is the guest message, and it can contain the guest
position
[string "..."]:line:. The library adds no position of its own to a guest error. An error that the library reports, such as an incorrect argument, contains the host position of the caller. Usepcall,chunk:pcallorfn:pcallto examine the error. Usechunk:xpcallwhen you need a guest traceback. - Host callbacks exchange primitives and guest tables. An argument can be a
primitive or a table. A table arrives as a live
lua.Tableview. A return value must be a primitive. - A hook and the JIT engine interact. A hook needs interpreted code. Therefore, the installation of a hook disables the JIT engine of the guest until the removal of the hook.
- Set the debug names. Give
chunkNametostate:load(), or usechunk:setName. Use the prefix@for a file path. Then a guest stack trace anddebug.getinfogive a usable name. - Bytecode is code. A chunk accepts bytecode by default, and bytecode runs
with the full power of the guest. Examine data from a source that you do not
trust with
chunk:isBytecode(), or refuse it withchunk:setMode("text").