-
-
Notifications
You must be signed in to change notification settings - Fork 2
Lua State
To create a Lua state, you can use any of the constructor overloads of the Lua
type.
using (var lua = new Lua())
{
// do stuff
}
Important
Lua
is disposable; disposing a Lua
instance closes the underlying Lua state and frees any allocated memory. You must dispose of unused Lua
states to free the allocated memory.
Note
In future examples, the wiki might omit the instantiation of Lua
and simply use a lua
variable which you're meant to replace with your instance.
Lua code is run by loading a chunk and then calling it. See Lua manual.
Lua
defines a few methods for running Lua code:
-
Execute()
- loads the specified Lua code and immediately calls it, without returning any values -
Evaluate()
- loads the specified Lua code and immediately calls it, returning the values the chunk may have returned-
Evaluate<T>()
can be used for a single value return
-
-
Load()
- loads the specified Lua code and returns it as aLuaFunction
, allowing for manual execution at a later point
lua.Execute("x = 2");
var result = lua.Evaluate<int>("return x + 2");
Console.WriteLine(result); // 4
Load()
is a bit more complex, as it returns a LuaFunction
which we have to call manually.
using (var chunk = lua.Load("x = x + 1 return x"))
{
for (var i = 0; i < 3; i++)
{
using (var results = chunk.Call())
{
result = results.First.GetValue<int>();
Console.WriteLine(result);
}
}
}
To pass variables between Lua code and .NET, global variables can be used. See Lua manual.
Lua
exposes multiple ways to get and set global variables within Lua:
-
TryGetGlobal<T>()
andGetGlobal<T>()
- retrieves the value of the global variable with the specified name -
SetGlobal<T>()
- sets the value of the global variable with the specified name -
Globals
property - returns aLuaTable
with the global variables
lua.SetGlobal("x", 42);
var x = lua.GetGlobal<int>("x");
Console.WriteLine(x); // 42
Lua.Globals
allows for more operations on the global variables:
var hasX = lua.Globals.ContainsKey("x");
Console.WriteLine(hasX); // True
var globalCount = lua.Globals.Count();
Console.WriteLine(globalCount); // 1
Lua offers a wide range of useful standard libraries. See Lua manual.
Laylua exposes these standard libraries through the LuaLibraries.Standard
nested static type.
For example, the mathematical functions library can be accessed via LuaLibraries.Standard.Math
.
Laylua utilizes the ILuaLibrary
interface to handle both standard libraries and custom libraries. This interface enables the opening and closing functionalities for the specified Lua instance, allowing you to manage the accessibility of libraries dynamically.
Opening all standard libraries:
lua.OpenStandardLibraries();
Getting the value of
lua.OpenLibrary(LuaLibraries.Standard.Math);
var pi = lua.Evaluate<double>("return math.pi");
Console.WriteLine(pi); // 3.141592653589793
When it comes to Lua
and its related entities, they follow the standard usage of exceptions such as ArgumentException
or InvalidOperationException
. However, there are specific exception types designed for handling failures in Lua interactions.
-
LuaException
serves as the base exception type that is thrown when an error occurs during Lua interaction. -
LuaPanicException
is the exception type used when an error happens in an unprotected environment and Lua triggers the panic handler. This type of exception is commonly associated with user code issues, although not always, and by default, it causes Lua to terminate the application. Laylua uses a customized panic handler that prevents the application from terminating and instead raises the exception. This behavior applies to both Windows and Unix systems.
lua.Execute("return x.y"); // LuaException: [string "?"]:1: attempt to index a nil value (global 'x')
Sandboxing in Laylua is very straightforward. This is mainly because when you create a Lua state the library does not inject anything into it. You have the freedom to selectively load specific libraries of your preference, meaning Lua only has access to whatever you let it have access to.
Additionally, Laylua comes with built-in features for memory allocation and instruction count limiting. This is done using custom allocators and hooks.
Warning
While there are various sandboxing options available in Lua and Laylua, it is important to note that unless the running code is isolated within a sandboxed virtual environment, you cannot reliably ensure that malicious code will not bypass these measures and potentially harm the host machine.
The Laylua.Moon.LuaAllocator
type is an unsafe
construct that can be used to control if and how memory is allocated by Lua. It essentially represents a callback Lua will invoke whenever it requires a chunk of memory to be allocated, reallocated, or deallocated.
Note
An allocator can be specified when instantiating a Lua state and cannot be changed to a different allocator afterwards.
Laylua comes with the built-in Laylua.Moon.NativeMemoryLuaAllocator
type which functions just like the default native memory allocator in Lua, but has the following additional features:
- maximum bytes allocated threshold
- allocation metrics
- allocation events
Creating a Lua state with memory allocation limits.
nuint maxBytes = 8 * 1024; // 8KiB
var allocator = new NativeMemoryLuaAllocator(maxBytes);
using (var lua = new Lua(allocator))
{
// LuaPanicException: 'not enough memory'
using (var largeTable = lua.CreateTable(4096))
{ }
}
The Laylua.Moon.LuaHook
type is an unsafe
construct that can be used to intercept code execution at specific points determined by the set event mask. It essentially represents a callback Lua can invoke whenever the following happen:
- function is called
- function returns
- new line of code is executed
- given amount of instructions was executed
Tip
Instruction count limiting allows you to easily prevent, for example, infinite loops in Lua code.
Note
A hook can be set at any given time on the low-level Lua state. Only one hook can be set at a time.
Laylua comes with the following built-in hook implementations:
-
Laylua.Moon.MaxInstructionCountLuaHook
which raises an error when the amount of instructions the Lua state is allowed to execute is exceeded -
Laylua.Moon.CancellationTokenLuaHook
which raises an error when cancellation is requested
using (var lua = new Lua())
{
lua.State.Hook = new MaxInstructionCountLuaHook(500);
lua.Execute("while true do end"); // LuaException: The maximum instruction count of 500 was exceeded by main code.
}
using (var cts = new CancellationTokenSource(50))
using (var lua = new Lua())
{
lua.State.Hook = new CancellationTokenLuaHook(cts.Token);
lua.Execute("while true do end"); // LuaException: The execution has been cancelled.
}