Jump to content

JLuaVM

From Advanced Computers Wiki
Revision as of 12:37, 22 August 2026 by GHXX (talk | contribs)

The JLuaVM, is the runtime that executes the Lua code that is running on computers in Advanced Computers. The source can be found on GitHub. The most important feature for Advanced Computers is the ability to save and restore the Lua execution state, regardless of what Lua code may be loaded. We are not aware of any Java based Lua runtime being capable of this, hence why this project was born in the first place.

While we try to keep things very similar, there are a few tiny differences, listed below. In the remainder of this article, LuaC refers to PUC-Rio's implementation of Lua. If you spot a difference between JLuaVM and LuaC which is not listed here, please open a ticket at https://github.com/MidnightMages/JLuaVM/issues with a snippet that functions differently between JLuaVM and LuaC.

LuaC deviations

In these cases JLuaVM (referred to as 'we') behaves differently than LuaC, but we still fulfill the Lua specification.

Area Description
Indexing of strings In LuaC indexing a string, where the index value someIndex can be an arbitrary value, always evaluates to nil. We argue that this is inconsistent and is almost never wanted behaviour. Therefore we instead throw an error just like indexing any other non-indexable type.
("someString")[someIndex]
Length of strings We intentionally return the number of characters instead of the length in bytes.
assert Produces a slightly different error message. Further, the message argument (second argument) is tostring()'ed before printing. This means that when the second argument happens to be a boolean, we properly print the value instead of simply stating that it was a boolean. The following snippet will raise an error (as the first argument is falsy) and the message is simply the constant true represented as a string.
assert(false, true)
ipairs In our case, ipairs only allows iterating over tables. LuaC would return nil when operating on a string and error when passing a type that is neither a string nor a table. For robustness we throw on all non-table arguments as it seems very unlikely that a string is passed intentionally.
pairs The order in which the items are returned may differ from LuaC.
table.insert In LuaC this function seems to behave differently depending on whether the table is a sequence or an actual dictionary. In our case table.insert behaves identical in both cases.
table.remove behaviour differs slightly as the table-length operator behaves differently.
Printing numbers We print numbers with a few more digits of precision.
Stacktraces are formatted differently Due to the lack of a loadfile function, the chunkname is directly fed into stacktraces, allowing for specifying filenames, etc. without being surrounded by quotes. Also some things are named slightly different within the stacktrace. This indirectly also affects the error(msg [, level]) function when msg is directly a string and level == nil or level > 0, and thus, part of the stacktrace is appended.

Lua language spec deviations

In this one case we do not follow the spec.

Area Description
math.randomseed(seed) This function does not accept two seeds, but instead only one. Similarly it also only returns one number. Extra arguments are ignored.

Lua language spec extensions

We have also slightly extended the existing specification.

OOP callable constants

The following expression is not syntactically allowed in LuaC: "someString":sub(1,3). In LuaC this would need to instead be written as ("someString"):sub(1,3). But as there is no grammatical conflict, we simply treat them the same.

Type Extension Methods

LuaC supports setting type-metatables using debug.setmetatable, e.g. for the string type. This, to our knowledge, however only works on a global level, so you cannot easily configure this on a per-environment (as in _ENV) setting.

We have therefore added support for what we call "Type Extension Methods", as they behave similarly to extension methods in C#. If an index operation fails and would normally result in an error, we try to perform a lookup in a subtable of _EXT.

For example, the following code "test":sub(1,3) is processed as follows:

  • "test" is a string, hence is not indexable, nor can it contain a metatable, so we immediately check for an extension function (or throw an index error otherwise)
  • Thus we check for _ENV["_EXT"]["string"]["sub"], which by default does exist, as _ENV._EXT.string is set to a table that contains most of the string-library functions.

In more general terms we access _ENV["_EXT][NAME OF TYPE][NAME OF FUNCTION] (where _ENV is often equivalent to _G). If no match is found, we simply raise the usual error.