|
1 | 1 | # node-lua-runner |
2 | 2 |
|
3 | | -> **Maintenance status:** This project is no longer actively maintained. |
4 | | -> It remains available as a standalone continuation of `medaeus245/node-lua` for users who need LuaJIT support with newer Node.js versions. |
| 3 | +Embed **Lua 5.1** in your Node.js programs. |
5 | 4 |
|
6 | | -This project started as a fork of `medaeus245/node-lua`, which appeared to be unmaintained. I updated it to compile with newer Node.js versions and tested it with Windows 10 and Node.js 18+. |
| 5 | +Lua and [LuaFileSystem](https://github.com/lunarmodules/luafilesystem) are compiled directly into |
| 6 | +the addon, so there is no system Lua to install and nothing to configure — `npm install` builds |
| 7 | +everything from source on Linux, macOS and Windows, on both x64 and ARM64. |
7 | 8 |
|
8 | | ---- |
| 9 | +## Installation |
9 | 10 |
|
10 | | -This Package allows you to use LUA inside Node.js. |
11 | | -See [`examples`](#examples). |
| 11 | +``` |
| 12 | +npm install node-lua-runner |
| 13 | +``` |
12 | 14 |
|
13 | | -I refactored the code so that it will compile with newer Node.JS versions. |
14 | | -I tested it with Windows 10 and Node.JS 18+. |
15 | | -Other Operating Systems or Combinations are not tested yet. |
| 15 | +The addon is compiled at install time, so you need a working C/C++ toolchain: |
16 | 16 |
|
17 | | -## Installation |
| 17 | +- **Linux** — `build-essential` (or your distribution's equivalent) and Python 3 |
| 18 | +- **macOS** — the Xcode Command Line Tools (`xcode-select --install`) |
| 19 | +- **Windows** — the "Desktop development with C++" workload from Visual Studio Build Tools |
18 | 20 |
|
19 | | -`npm install node-lua-runner` |
| 21 | +Nothing else. Earlier versions needed you to build and install LuaJIT yourself on Linux; that is |
| 22 | +no longer the case. |
20 | 23 |
|
21 | | -## 1.1.0 2023-04-21 |
| 24 | +> **Windows with Visual Studio 2026:** `node-gyp` 11.x cannot detect that version and fails with |
| 25 | +> `find VS unknown version "undefined"`. It is what npm bundles on Node.js 20 and 22. Install |
| 26 | +> node-gyp 12 and point npm at it — note the explicit `@12`, since `@latest` still resolves to |
| 27 | +> 11.x on those Node versions: |
| 28 | +> |
| 29 | +> ``` |
| 30 | +> npm install -g node-gyp@12 |
| 31 | +> set npm_config_node_gyp=%APPDATA%\npm\node_modules\node-gyp\bin\node-gyp.js |
| 32 | +> npm install node-lua-runner |
| 33 | +> ``` |
22 | 34 |
|
23 | | -- Added LuaFileSystem for Windows (x64) compiled against luajit. See Examples for usage. |
| 35 | +## Quick start |
24 | 36 |
|
25 | | -## About |
| 37 | +```javascript |
| 38 | +const nodelua = require('node-lua-runner'); |
26 | 39 |
|
| 40 | +const lua = new nodelua.LuaState(); |
27 | 41 |
|
28 | | -Using Lua5.1 C interface: https://www.lua.org/manual/5.1/manual.html with luajit compiler |
| 42 | +lua.DoString('print("Hello from Lua!")'); |
29 | 43 |
|
30 | | -**Based on:** |
31 | | - - nodelua ( https://github.com/brettlangdon/NodeLua ) |
32 | | - - node-luajit ( https://github.com/whtiehack/node-luajit ) |
33 | | - - LuaFileSystem ( https://github.com/lunarmodules/luafilesystem ) |
| 44 | +// Expose a JavaScript function to Lua |
| 45 | +lua.RegisterFunction('add', function () { |
| 46 | + const a = lua.ToValue(1); |
| 47 | + const b = lua.ToValue(2); |
| 48 | + lua.Pop(2); |
| 49 | + lua.Push(a + b); |
| 50 | + return 1; |
| 51 | +}); |
34 | 52 |
|
35 | | -**Features:** |
36 | | -- Low-Level API |
37 | | -- For now Sync only |
| 53 | +lua.DoString('print("2 + 3 = " .. add(2, 3))'); |
| 54 | +
|
| 55 | +lua.Close(); |
| 56 | +``` |
| 57 | +
|
| 58 | +## Breaking changes in 2.0.0 |
38 | 59 |
|
| 60 | +2.0.0 is a maintenance release that makes the package build and run on current Node.js. It fixes |
| 61 | +several long-standing bugs, and those fixes change behaviour: |
39 | 62 |
|
40 | | -**Compilation:** |
41 | | -- On Windows and Mac : Luajit library already included in the package |
42 | | -- On Linux: Compilation with following parameters: |
43 | | - - Include directory: (find /usr/include /usr/local/include $NODELUA_INCLUDE -name lua.h | sed s/lua.h//) |
44 | | - - Library directory: "/usr/local/lib" |
45 | | - - Library: "/usr/local/lib/libluajit-5.1.so" |
| 63 | +| Change | Before | Now | |
| 64 | +|---|---|---| |
| 65 | +| `SetField(index, key, value)` | Wrote the *key* into the field, ignoring the value | Writes the value, and resolves a relative `index` before pushing | |
| 66 | +| `LoadFile` / `LoadString` | Executed the chunk, identical to `DoFile` / `DoString` | Compile and push the chunk without running it; call `Call(0, 0)` to run it | |
| 67 | +| Lua booleans read into JS | Arrived as the numbers `1` and `0` | Arrive as `true` and `false` | |
| 68 | +| `Push(3.5)` | Truncated to `3` | Keeps the fractional value | |
| 69 | +| `AddPackagePath` | Appended without a separator, corrupting `package.path`, so `require` usually failed | Appends correctly, and no longer breaks on paths containing quotes | |
| 70 | +| `ToValue` on a table | Only converted correctly when the table was at the top of the stack | Works at any stack index, including nested tables | |
| 71 | +| Calling a method after `Close()` | Use-after-free | Throws | |
| 72 | +| `Resume(args)` | Returned `undefined` | Returns the Lua status code | |
46 | 73 |
|
47 | | -NOTE (Linux only): Don't forget to set your LD_LIBRARY_PATH to /usr/local/lib so that node-lua can find luajit. |
| 74 | +Other things worth knowing if you are upgrading: |
| 75 | + |
| 76 | +- **LuaJIT has been replaced by stock Lua 5.1.5.** Windows builds used to link LuaJIT 2.0.3, so |
| 77 | + Windows users lose JIT compilation. In exchange, macOS on Apple Silicon and Linux on ARM64 work |
| 78 | + at all, which they previously did not. The Lua C API and language are unchanged. |
| 79 | +- **`require('lfs')` now works on every platform.** It used to be a Windows-only prebuilt DLL |
| 80 | + loaded through an `LUA_CPATH` hack; LuaFileSystem is now compiled into the addon. |
| 81 | +- Node.js 18 or newer is required. |
| 82 | + |
| 83 | +## About |
| 84 | + |
| 85 | +Built on the [Lua 5.1 C API](https://www.lua.org/manual/5.1/manual.html). The binding uses |
| 86 | +[Node-API](https://nodejs.org/api/n-api.html), which is ABI-stable — a compiled build keeps |
| 87 | +working across future Node.js releases instead of breaking on each major version. |
| 88 | + |
| 89 | +**Based on:** |
| 90 | +- nodelua ( https://github.com/brettlangdon/NodeLua ) |
| 91 | +- node-luajit ( https://github.com/whtiehack/node-luajit ) |
| 92 | +- LuaFileSystem ( https://github.com/lunarmodules/luafilesystem ) |
| 93 | + |
| 94 | +**Features:** |
| 95 | +- Low-level API mapping closely onto the Lua C API |
| 96 | +- Synchronous only |
48 | 97 |
|
49 | 98 | ## Examples |
50 | 99 |
|
51 | | -- [Simple](https://github.com/0x7878/node-lua-runner/blob/master/examples/simple/index.js) |
52 | | -- [Using lua require function](https://github.com/0x7878/node-lua-runner/blob/master/examples/lua_require/index.js) |
53 | | -- [using lua file system](https://github.com/0x7878/node-lua-runner/tree/master/examples/lua_lfs) |
| 100 | +- [Simple](https://github.com/mschmicking/node-lua-runner/blob/master/examples/simple/index.js) |
| 101 | +- [Using the lua require function](https://github.com/mschmicking/node-lua-runner/blob/master/examples/lua_require/index.js) |
| 102 | +- [Using LuaFileSystem](https://github.com/mschmicking/node-lua-runner/tree/master/examples/lua_lfs) |
54 | 103 |
|
55 | 104 | ## API |
56 | 105 |
|
57 | | - |
58 | 106 | ```javascript |
59 | 107 |
|
60 | 108 | const nodelua = require('node-lua-runner'); |
@@ -86,7 +134,27 @@ lua.DoString("print('Hello world!')"); |
86 | 134 |
|
87 | 135 |
|
88 | 136 | /** |
89 | | - * [Sets the function f as the new value of global name] |
| 137 | + * [Compiles the given file and pushes it onto the stack WITHOUT running it. |
| 138 | + * Use Call to run it.] |
| 139 | + * @type {String} file |
| 140 | + * @throws {Exception} |
| 141 | + */ |
| 142 | +lua.LoadFile(__dirname + "/test.lua"); |
| 143 | + |
| 144 | + |
| 145 | +/** |
| 146 | + * [Compiles the given string and pushes it onto the stack WITHOUT running it. |
| 147 | + * Use Call to run it.] |
| 148 | + * @type {String} str |
| 149 | + * @throws {Exception} |
| 150 | + */ |
| 151 | +lua.LoadString("print('Hello world!')"); |
| 152 | + |
| 153 | + |
| 154 | +/** |
| 155 | + * [Sets the function f as the new value of global name. |
| 156 | + * Arguments are read off the Lua stack with ToValue; the return value is the |
| 157 | + * number of results the function pushed.] |
90 | 158 | * @param {String} name [name of the global in lua] |
91 | 159 | * @param {Function} f [function to set] |
92 | 160 | */ |
@@ -114,19 +182,24 @@ lua.GetGlobal('myVar'); |
114 | 182 |
|
115 | 183 |
|
116 | 184 | /** |
117 | | - * [Does the equivalent to t[k] = v, where t is the value at the given valid index and v is the value at the top of the stack. This function pops the value from the stack.] |
| 185 | + * [Does the equivalent to t[k] = v, where t is the value at the given valid index. |
| 186 | + * Unlike the raw C API, v is passed as an argument rather than taken from the |
| 187 | + * top of the stack.] |
118 | 188 | * @type {Number} index |
119 | 189 | * @type {String} key |
| 190 | + * @type {*} value |
| 191 | + * @throws {Exception} [if the value at index is not a table] |
120 | 192 | */ |
121 | | -lua.SetField(index, "t"); |
| 193 | +lua.SetField(index, "key", value); |
122 | 194 |
|
123 | 195 |
|
124 | 196 | /** |
125 | 197 | * [Pushes onto the stack the value t[key], where t is the value at the given valid index.] |
126 | 198 | * @type {Number} index |
127 | 199 | * @type {String} key |
| 200 | + * @throws {Exception} [if the value at index is not a table] |
128 | 201 | */ |
129 | | -lua.GetField(index, "t"); |
| 202 | +lua.GetField(index, "key"); |
130 | 203 |
|
131 | 204 |
|
132 | 205 | /** |
@@ -156,6 +229,7 @@ lua.Yield(args); |
156 | 229 | /** |
157 | 230 | * [Starts and resumes a coroutine in a given thread.] |
158 | 231 | * @type {Number} args |
| 232 | + * @return {Number} [status code] |
159 | 233 | */ |
160 | 234 | lua.Resume(args); |
161 | 235 |
|
@@ -196,4 +270,70 @@ lua.SetTop(index); |
196 | 270 | lua.Replace(index); |
197 | 271 |
|
198 | 272 |
|
| 273 | +/** |
| 274 | + * [Returns the status of the state] |
| 275 | + * @return {Number} [compare against nodelua.STATUS.*] |
| 276 | + */ |
| 277 | +lua.Status(); |
| 278 | + |
| 279 | + |
| 280 | +/** |
| 281 | + * [Controls the Lua garbage collector] |
| 282 | + * @type {Number} what [one of nodelua.GC.*] |
| 283 | + * @return {Number} |
| 284 | + */ |
| 285 | +lua.CollectGarbage(nodelua.GC.COLLECT); |
| 286 | + |
| 287 | + |
| 288 | +/** |
| 289 | + * [Destroys the Lua state and frees its memory. Safe to call more than once. |
| 290 | + * Any further use of the state throws.] |
| 291 | + */ |
| 292 | +lua.Close(); |
| 293 | + |
| 294 | +``` |
| 295 | + |
| 296 | +### Constants |
| 297 | + |
| 298 | +```javascript |
| 299 | +nodelua.INFO.VERSION // "Lua 5.1" |
| 300 | +nodelua.INFO.VERSION_NUM // 501 |
| 301 | +nodelua.INFO.COPYRIGHT |
| 302 | +nodelua.INFO.AUTHORS |
| 303 | + |
| 304 | +nodelua.LUA.GLOBALSINDEX // pseudo-index of the globals table |
| 305 | +nodelua.LUA.REGISTRYINDEX // pseudo-index of the registry |
| 306 | + |
| 307 | +nodelua.STATUS.YIELD |
| 308 | +nodelua.STATUS.ERRRUN |
| 309 | +nodelua.STATUS.ERRSYNTAX |
| 310 | +nodelua.STATUS.ERRMEM |
| 311 | +nodelua.STATUS.ERRERR |
| 312 | + |
| 313 | +nodelua.GC.STOP |
| 314 | +nodelua.GC.RESTART |
| 315 | +nodelua.GC.COLLECT |
| 316 | +nodelua.GC.COUNT |
| 317 | +nodelua.GC.COUNTB |
| 318 | +nodelua.GC.STEP |
| 319 | +nodelua.GC.SETPAUSE |
| 320 | +nodelua.GC.SETSTEPMUL |
199 | 321 | ``` |
| 322 | + |
| 323 | +## Caveats |
| 324 | + |
| 325 | +This is a thin wrapper over the Lua C API, and it does not shield you from every way of misusing |
| 326 | +that API. Some stack operations on values of an unexpected type raise an *unprotected* Lua error, |
| 327 | +which aborts the process rather than throwing a JavaScript exception. `SetField` and `GetField` |
| 328 | +guard against this explicitly; other methods do not. Keep track of what is on the stack. |
| 329 | + |
| 330 | +## Development |
| 331 | + |
| 332 | +``` |
| 333 | +npm install |
| 334 | +npm test |
| 335 | +``` |
| 336 | + |
| 337 | +## License |
| 338 | + |
| 339 | +ISC — see [LICENSE.md](LICENSE.md), which also covers the vendored Lua and LuaFileSystem sources. |
0 commit comments