Document the Lua environment (#2)

This commit is contained in:
Lars Müller 2022-01-01 21:33:10 +01:00 committed by GitHub
parent 64d80b565e
commit 9a58519205
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

252
doc/environment.adoc Normal file

@ -0,0 +1,252 @@
= Lua Environment
:description: Documentation of the global environment Minetest mods run in
:keywords: lua, luajit, environment, mod, security, portability, platform, library
:toc:
:url-reference-manual: https://www.lua.org/manual/5.1/manual.html
Minetest uses Lua 5.1. The environment in which Minetest executes mods depends on four factors:
. The operating system
. The Minetest build: LuaJIT or PUC Lua 5.1
. The mod type: Client-side or server-side
. The mod environment: Secure or insecure
== Platform Independence / Portability
See the {url-reference-manual}[Lua 5.1 Reference Manual] for platform- and OS-environment-dependant Lua features. These include:
* Locale, affecting pattern matching (character classes) and character codes used by `string.char` and `string.byte`
* Large parts of the `os` library, particularly `os.execute` (only available in an insecure environment)
* Some parts of the `io` library like `io.popen` (only available in an insecure environment) or handling of binary files
* `require` and `package.loadlib` (only available in an insecure environment anyways)
== Standard Library Extensions
=== `math`
==== `math.hypot(x, y)`
Finds the length of the hypotenuse `z` according to the Pythagorean Theorem: `z^2 = x^2 + y^2`. Shorthand for `math.sqrt(x*x + y*y)`.
==== `math.sign(x, [tolerance])`
`tolerance` defaults to `0` if falsy. Returns `-1` if the value is smaller than the `tolerance`, `1` if it is larger. Returns `0` if `x` is within the closed tolerance interval `[-tolerance, tolerance]`. Also returns `0` if `x` is `nan`.
==== `math.factorial(x)`
`x` must be a non-negative integer; otherwise, the function will error with `"factorial expects a non-negative integer"`. If `x` is at least `171`, `+inf` is returned.
As Python has built-in big integer support (and uses 64-bit `float`), it can be used to easily determine for which `x` this implementation becomes imprecise due to float precision limitations:
[source,python3]
----
def factorial(x):
return x if x == 1 else x * factorial(x-1)
for x in range(1, 171):
if factorial(float(x)) != factorial(x):
print(x)
break
----
This will print `23`. This means that only for `x` values ranging from `1` to `22`, both inclusive, `factorial(x)` will be fully accurate.
==== `math.round(x)`
Rounds `x` towards the nearest integer value. Edge cases:
* Ties: If `x` is exactly the same distance from two integer values (`x = k + 0.5`) with `k` being an integer, it is rounded "away from zero", to the value with the higher absolute value:
** If `x > 0`, `x` will be rounded to the larger value;
** If `x < 0`, `x` will be rounded to the smaller value.
* Precision: Numbers very close to ties (plus/minus `0.49999999999999994`) are incorrectly handled like ties. See https://stackoverflow.com/a/58411671/7185318[this StackOverflow answer on rounding in Lua].
* Special float values: `nan` and `inf` are preserved, as well as their sign.
=== `string`
These functions can be used over the string metatable as well, using `self:func(...)` if `self` is a string.
Note. `"...":func(...)` is a syntax error in Lua. Wrap strings in brackets `(...)` if you want to index them: `("..."):func(...)`.
==== `string.trim(self)`
Will return a string with consecutive spacing characters (`%s` pattern character class) at the start and the end of the string removed. Usually the following characters are considered spacing:
* Horizontal tab: `'\t'` or `'\9'`
* Newline: `'\n'` or `'\10'`
* Vertical tab: `'\v'` or `'\11'`
* Form feed: `'\f'` or `'\12'`
* Carriage return: `'\r'` or `'\13'`
* Space: `' '` or `'\32'`
As determined using the below Lua script, which outputs the decimal character codes:
[source,lua]
----
for i = 0, 255 do
if string.char(i):match"%s" then
print(i)
end
end
----
WARNING: Platform-independence is not guaranteed: "The definitions of letter, space, and other character groups depend on the current locale." - https://www.lua.org/manual/5.1/manual.html#5.4.1[Lua 5.1 Reference Manual, section 5.4.1]
==== `string.split(str, [delim], [include_empty], [max_splits], [sep_is_pattern])`
* `str`: The string to split.
* `delim`: Delimiter/separator. Defaults to `","` if falsy.
* `include_empty`: If truthy, empty strings (`""`) are included in the returned list.
* `max_splits`: Maximum amount of splits to be done. Splits are done in left-to-right (string start to end) order. The resulting list can have up to `max_splits + 1` entries. The last element in the list may contain the delimiter. Unlimited splits if falsy, negative, `nan` or `+inf`.
* `sep_is_pattern`: If truthy, `delim` is used as a pattern.
Returns a list containing the delimited parts without the delimiters.
=== `table`
==== `table.indexof(list, val)`
Linear search for `val` in the `list`. Returns the first index where the value equals `val`. Returns `-1` if the value is not found.
==== `table.copy(t, [seen])`
Deepcopies the table `t` and all it's subtables - both keys and values. Non-table types are not copied, even if they are reference types (userdata, functions and threads). The reference structure will be fully preserved: A single table, even if referenced multiple times, will only be copied a single time; subsequent references in the copy will just reference the same copied table.
The `seen` table is a lookup for already copied tables, which are used as keys. The value is the copy. By providing `[table] = table` entries for certain tables, you can prevent them from being copied.
==== `table.insert_all(t, other)`
Adds all the list entries of `other` to `t` (list part concatenation).
==== `table.key_value_swap(t)`
Returns a new table with the keys of `t` as values and the corresponding values as keys. If a value occurs multiple times in `t`, any of the keys might be the value in the resulting table.
==== `table.shuffle(t, from, to, random)`
Performs a Fisher-Yates shuffling on the specified range of the list part of `t`.
* `from`: Inclusive starting index of the range to be shuffled. Defaults to the first item of the list part if falsy.
* `to`: Inclusive end index of the range to be shuffled. Defaults to the last item of the list part if falsy.
* `random`: A `function(from, to)` that returns a random integer in the specified range, with both `from` and `to` inclusive. Defaults to `math.random` if falsy.
Returns nothing.
== LuaJIT extensions
Minetest builds compiled with LuaJIT (`ENABLE_LUAJIT=1`) provide the https://luajit.org/extensions.html[LuaJIT extensions]. These include syntactical Lua 5.2 language features like `goto`, which will lead to a syntax error on PUC Lua 5.1. Hex escapes will be converted into the raw characters by PUC Lua 5.1.
== Common extensions
https://bitop.luajit.org/[LuaJIT's `bit` library] is made available for both PUC Lua and LuaJIT builds. It must not be required, as this will lead to a crash in a secure environment as documented below; in an insecure environment, it is simply unneeded.
== Secure environment whitelists
In the secure environment, the following builtin Lua(JIT) libraries and library functions are whitelisted:
* `_VERSION`
* Garbage collection: `collectgarbage`
* Cooperative multithreading: `coroutine`
* Error handling:
** `assert`
** `error`
** `pcall`
** `xpcall`
* Function environments:
** `setfenv`
** `getfenv`
* `math`
* `string`
* Tables:
** `table`
** Iteration:
*** `next`
*** `pairs`
*** `ipairs`
* Metatables:
** `setmetatable`
** `getmetatable` (SSM-only)
** Raw methods:
*** `rawset`
*** `rawget`
*** `rawequals`
* Varargs:
** `select`
** `unpack`
* Conversion:
** `tostring`
** `tonumber`
* `type`
* Output: `print`
Some library tables are restricted by whitelists as well:
* `io` (SSM-only)
** `read`
** `write`
** `flush`
** `close`
** `type`
* `os`: Mostly time-related functions
** `clock`
** `date`
** `difftime`
** `time`
** SSM-only:
*** `getenv`
*** `setlocale`
*** `tmpname`
* `debug`:
** `gethook`
** `traceback`
** SSM-only:
*** `getinfo`
*** `getmetatable`
*** `setmetatable`
*** `upvalueid`
*** `sethook`
*** `debug`
* `package` (SSM-only):
** `config`
** `cpath`
** `path`
** `searchpath`
* `jit` (LuaJIT-only):
** `arch`
** `flush`
** `off`
** `on`
** `opt`
** `os`
** `status`
** `version`
** `version_num`
Everything file-related is replaced by a secure variant:
* Loading Lua code: Errors with `"Bytecode prohibited when mod security is enabled."` if the sources are bytecode (strings starting with `'\27'`). For CSM, the file-related functions operate on virtual paths and only have access to CSM files.
** `dofile`
** `load`
** `loadfile`
** `loadstring`
** `require`: Disabled, errors with `"require() is disabled when mod security is on."`.
The following functions, which are *not available to CSM / SSM-only*, allow read-only access to all mod directories and write access to the current loading mod's directory only while it's loading (a handle with write access to a file within the mod directory can however be stored and used at a later time) and read & write access to the world directory excepting the `worldmods` and `game` subfolders:
* `io`:
** `open`
** `input`
** `output`
** `lines`
* `os`:
** `remove`
** `rename`
Builtin can read and write anywhere during it's load time.
See the {url-reference-manual}[Lua 5.1 Reference Manual] for documentation of the Lua standard library.
If mod security is disabled, server-side mods run in an insecure environment, which contains all libraries and library functions, without any restrictions. The same restrictions apply to trusted server-side mods, which can however request an insecure environment in table form using `minetest.request_insecure_environment` which will contain shallow copies of library tables and no global restrictions.
// TODO link minetest.request_insecure_environment