JLuaVM: Difference between revisions
No edit summary |
|||
| (10 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
The JLuaVM, is the runtime that executes the Lua code that is running on computers in [[Advanced Computers]]. The source can be found [https://github.com/MidnightMages/JLuaVM on GitHub]. 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. | The JLuaVM, is the runtime that executes the Lua code that is running on computers in [[Advanced Computers]]. The source can be found [https://github.com/MidnightMages/JLuaVM 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. | |||
If you would like to contribute please see https://github.com/MidnightMages/JLuaVM/tree/master#contributing. | |||
== LuaC deviations == | == LuaC deviations == | ||
| Line 18: | Line 22: | ||
|- | |- | ||
|assert | |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.<syntaxhighlight lang="lua" line="1"> | |Produces a slightly different error message. Further, the message argument (second argument) is <code>tostring()</code>'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 <code>true</code> represented as a string.<syntaxhighlight lang="lua" line="1"> | |||
assert(false, true) | assert(false, true) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 42: | Line 47: | ||
== Lua language spec deviations == | == Lua language spec deviations == | ||
In this one case we '''do not''' follow the spec. | |||
{| class="wikitable" | |||
!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 == | == Lua language spec extensions == | ||
We have also slightly extended the existing specification. | |||
=== OOP callable constants === | |||
The following expression is not syntactically allowed in LuaC: <code>"someString":sub(1,3)</code>. In LuaC this would need to instead be written as <code>("someString"):sub(1,3)</code>. But as there is no grammatical conflict, we simply treat them the same. | |||
=== Type Extension Methods === | |||
LuaC supports setting type-metatables using <code>debug.setmetatable</code>, 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 <code>_ENV</code>) 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 <code>_EXT</code>. | |||
For example, the following code <code>"test":sub(1,3)</code> 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 <code>_ENV["_EXT"]["string"]["sub"]</code>, which by default does exist, as <code>_ENV._EXT.string</code> is set to a table that contains most of the string-library functions. | |||
In more general terms we access <code>_ENV["_EXT][NAME OF TYPE][NAME OF FUNCTION]</code> (where <code>_ENV</code> is often equivalent to <code>_G</code>). If no match is found, we simply raise the usual error. | |||
== Additional standard library functions == | |||
This is a list of all additional callable, non-standard functions provided by JLuaVM. | |||
{| class="wikitable" | |||
!Function | |||
!Returns | |||
!Description | |||
|- | |||
|<code>vm.listUdKeys(obj:userdata)</code> | |||
|<code>table</code> | |||
|Returns a table whose keys are all values that are readable or writable in the given userdata object <code>obj</code>. The value of each key is either <code>r</code>, <code>w</code> or <code>rw</code>, depending on whether the key is readable writable or readwrite. | |||
|- | |||
| rowspan="3" |<code>coroutine.resumeWithTimeout(co:thread, timeout:number, ...)</code> | |||
| style="padding:0;" |<code>true, ...</code> | |||
|The coroutine has yielded on its own. Any arguments supplied to <code>coroutine.yield(...)</code>, are returned after <code>true</code>. | |||
|- | |||
|<code>false, any:errorMessage</code> | |||
|The coroutine has errored. | |||
|- | |||
|<code>"preempted"</code> | |||
|The coroutine has exceeded the execution <code>timeout</code> (given in seconds) and has been forcefully paused. In this event <code>coroutine.status(co)</code> will return <code>"preempted_resumable"</code>. Any child coroutines that were currently running at the time of yielding will enter the state <code>"preempted_blocked"</code>. Attempting to <code>resume()</code> such <code>preempted_blocked</code> coroutines will throw an error. | |||
This is intended as a last-resort mechanism for task schedulers to interrupt uncooperative processes. | |||
|- | |||
|<code>coroutine.status(co:thread)</code> | |||
|<code>string</code> | |||
|For completeness, this function may return all of the LuaC states <code>suspended</code>, <code>running</code>, <code>normal</code> and <code>dead</code> as well as <code>preempted_resumable</code> and <code>preempted_blocked</code>. | |||
Resumable states are <code>suspended</code> and <code>preempted_resumable</code>. | |||
|} | |||
Further functionality that is NOT available in [[Advanced Computers]]. | |||
{| class="wikitable" | |||
!Function | |||
!Returns | |||
!Description | |||
|- | |||
|<code>vm.pause()</code> | |||
|<code>void</code> | |||
|Immediately pauses the current LuaVM. | |||
|} | |||
{{Navbox content}} | |||
Latest revision as of 16:33, 25 August 2026
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.
If you would like to contribute please see https://github.com/MidnightMages/JLuaVM/tree/master#contributing.
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.stringis 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.
Additional standard library functions
This is a list of all additional callable, non-standard functions provided by JLuaVM.
| Function | Returns | Description |
|---|---|---|
vm.listUdKeys(obj:userdata)
|
table
|
Returns a table whose keys are all values that are readable or writable in the given userdata object obj. The value of each key is either r, w or rw, depending on whether the key is readable writable or readwrite.
|
coroutine.resumeWithTimeout(co:thread, timeout:number, ...)
|
true, ...
|
The coroutine has yielded on its own. Any arguments supplied to coroutine.yield(...), are returned after true.
|
false, any:errorMessage
|
The coroutine has errored. | |
"preempted"
|
The coroutine has exceeded the execution timeout (given in seconds) and has been forcefully paused. In this event coroutine.status(co) will return "preempted_resumable". Any child coroutines that were currently running at the time of yielding will enter the state "preempted_blocked". Attempting to resume() such preempted_blocked coroutines will throw an error.
This is intended as a last-resort mechanism for task schedulers to interrupt uncooperative processes. | |
coroutine.status(co:thread)
|
string
|
For completeness, this function may return all of the LuaC states suspended, running, normal and dead as well as preempted_resumable and preempted_blocked.
Resumable states are |
Further functionality that is NOT available in Advanced Computers.
| Function | Returns | Description |
|---|---|---|
vm.pause()
|
void
|
Immediately pauses the current LuaVM. |