├── .github
└── workflows
│ └── ci.yaml
├── .gitignore
├── LICENSE
├── NEWS
├── README.md
├── logo.png
├── logo.sketch
├── package.json
├── src
├── defs.js
├── fengari.js
├── fengaricore.js
├── fengarilib.js
├── lapi.js
├── lauxlib.js
├── lbaselib.js
├── lcode.js
├── lcorolib.js
├── ldblib.js
├── ldebug.js
├── ldo.js
├── ldump.js
├── lfunc.js
├── linit.js
├── liolib.js
├── ljstype.js
├── llex.js
├── llimits.js
├── lmathlib.js
├── loadlib.js
├── lobject.js
├── lopcodes.js
├── loslib.js
├── lparser.js
├── lstate.js
├── lstring.js
├── lstrlib.js
├── ltable.js
├── ltablib.js
├── ltm.js
├── lua.js
├── luaconf.js
├── lualib.js
├── lundump.js
├── lutf8lib.js
├── lvm.js
└── lzio.js
├── test
├── defs.test.js
├── lapi.test.js
├── lauxlib.test.js
├── lbaselib.test.js
├── lcorolib.test.js
├── ldblib.test.js
├── ldebug.test.js
├── lexparse.test.js
├── lib-hello.js.mod
├── lmathlib.test.js
├── load.test.js
├── loadfile-test.bc
├── loadfile-test.lua
├── loadlib.test.js
├── loslib.test.js
├── lstrlib.test.js
├── ltablib.test.js
├── ltm.test.js
├── lua.test.js
├── lutf8lib.test.js
├── lvm.test.js
├── manual-tests
│ └── debug-cli.js
├── module-hello.lua
├── test-suite
│ ├── api.test.js
│ ├── attrib.test.js
│ ├── bitwise.test.js
│ ├── calls.test.js
│ ├── closure.test.js
│ ├── code.test.js
│ ├── constructs.test.js
│ ├── coroutine.test.js
│ ├── db.test.js
│ ├── errors.test.js
│ ├── events.test.js
│ ├── goto.test.js
│ ├── literals.test.js
│ ├── locals.test.js
│ ├── ltests.js
│ ├── math.test.js
│ ├── nextvar.test.js
│ ├── pm-classes.lua
│ ├── pm-gsub.lua
│ ├── pm.test.js
│ ├── sort.test.js
│ ├── strings.test.js
│ ├── tpack.test.js
│ ├── utf8.test.js
│ └── vararg.test.js
└── tests.js
└── yarn.lock
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | strategy:
15 | matrix:
16 | node-version: [12.x, 14.x, 16.x, 17.x]
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v2
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | cache: 'yarn'
25 | - name: Install dependencies
26 | run: yarn
27 | - run: yarn lint
28 | - run: yarn test --maxWorkers=$(nproc)
29 | env:
30 | FENGARICONF: '{"LUAI_MAXSTACK":100000}'
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # JSDoc
40 | help
41 |
42 | *.out
43 | *.dSYM
44 |
45 | # Sublime
46 | *.sublime-project
47 | *.sublime-workspace
48 |
49 | .gdb_history
50 |
51 | dist
52 |
53 | .tern-port
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright © 2017-2019 Benoit Giannangeli
4 | Copyright © 2017-2024 Daurnimator
5 | Copyright © 1994–2017 Lua.org, PUC-Rio.
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/NEWS:
--------------------------------------------------------------------------------
1 | UNRELEASED
2 |
3 | - Add os.setlocale that only understands the C locale
4 | - Fix incorrect length for certain tables
5 | - Remove luai_apicheck
6 |
7 |
8 | 0.1.4 - 2018-11-28
9 |
10 | - Fix bug where nil values are not removed from tables
11 |
12 |
13 | 0.1.3 - 2018-10-29
14 |
15 | - Fix loading bytecode with empty string constant
16 | - Fix missing luaopen_base export
17 | - Use only C locale for string->number conversions
18 |
19 |
20 | 0.1.2 - 2018-04-22
21 |
22 | - More node 6 compatibility fixes
23 | - Use indirect eval to get reference to global environment
24 | - Move test suite to jest
25 | - Fixes for os.time and os.date normalisation
26 | - Use own strftime code (drops strftime npm package dependency)
27 | - Fix passing JavaScript strings to luaL_add(l)string
28 |
29 |
30 | 0.1.1 - 2018-04-03
31 |
32 | - Fix node 6 compatibility
33 | - Removed duplicate LUA_PATH entries
34 |
35 |
36 | 0.1.0 - 2018-03-31
37 |
38 | - Fengari core implements Lua 5.3 in ES6
39 | - Implements the Lua C API
40 | - Lua standard library implemented on top of "C" API
41 | - Works in node.js
42 | - Works in browsers (including IE11+, Safari 8+, Chrome, Firefox and Edge)
43 | - Lua test suite ported to JavaScript and passes (excluding items below)
44 | - Known incompatability: garbage collector cannot be controlled
45 | - Known incompatability: __gc not respected
46 | - Known incompatability: __mode not respected
47 | - Known incompatability: io library not available in browsers
48 | - Known incompatability: io library unable to open new files
49 | - Known incompatability: io library read functionality is not implemented
50 | - Known incompatability: some os library functions are not implemented
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/fengari-lua/fengari/actions/workflows/ci.yaml?query=event%3Apush)
2 | [](https://npmjs.com/package/fengari)
3 | [](https://opensource.org/licenses/MIT)
4 | [](https://web.libera.chat/?channels=#fengari)
5 | [](https://lgtm.com/projects/g/fengari-lua/fengari/context:javascript)
6 | [](https://lgtm.com/projects/g/fengari-lua/fengari/alerts)
7 |
8 |
9 |
10 |
11 |
12 |
13 | # Fengari
14 |
15 | 🌙 φεγγάρι - The Lua VM written in JS ES6 for Node and the browser
16 |
17 | This repository contains the core fengari code (which is a port of the Lua C library) which includes parser, virtual machine and base libraries.
18 | However it is rare to use this repository directly.
19 |
20 | - To use fengari in a web browser as easily as you might use JavaScript, see [fengari-web](https://github.com/fengari-lua/fengari-web)
21 | - [fengari-interop](https://github.com/fengari-lua/fengari-interop) is a lua library that makes interoperating with JavaScript objects simple, it is already included in fengari-web.
22 | - For a clone of the `lua` command line tool, but running under node.js, see [fengari-node-cli](https://github.com/fengari-lua/fengari-node-cli)
23 |
24 | ### The JS API
25 |
26 | Once you've loaded fengari, you can use the JS API:
27 |
28 | ```js
29 | const luaconf = fengari.luaconf;
30 | const lua = fengari.lua;
31 | const lauxlib = fengari.lauxlib;
32 | const lualib = fengari.lualib;
33 |
34 | const L = lauxlib.luaL_newstate();
35 |
36 | lualib.luaL_openlibs(L);
37 |
38 | lua.lua_pushliteral(L, "hello world!");
39 | ```
40 |
41 | The JS API is exactly the same as the C API so `fengari.lua` exposes the same constants and functions as `lua.h`, `fengari.lauxlib` the same as `lauxlib.h` and `fengari.lualib` the same as `lualib.h`. If you're unfamiliar with the C API, you can take a look at [the manual](http://www.lua.org/manual/5.3/manual.html#4).
42 |
43 |
44 | ## Semantics
45 |
46 | Fengari implements Lua 5.3 semantics and will hopefully follow future Lua releases. If you find any noticeable difference between Fengari and Lua's behaviours, please [report it](https://github.com/fengari-lua/fengari/issues).
47 |
48 | ### Strings
49 |
50 | Lua strings are 8-bits clean and can embed `\0`. Which means that invalid UTF-8/16 strings are valid Lua strings. Lua functions like `string.dump` even use strings as a way of storing binary data.
51 |
52 | To address that issue, Fengari uses [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) objects containing the raw bytes to implement lua strings. To push a JS string on the stack you can use `lua_pushliteral` which will convert it to an array of bytes before pushing it. To get a Lua string on the stack as a JS string you can use `lua_tojsstring` which will attempt to convert it to a UTF-16 JS string. The latter won't give you what you expect if the Lua string is not a valid UTF-16 sequence. You can also convert strings with `luastring_of`, `to_luastring`, `to_jsstring` and `to_uristring`.
53 |
54 |
55 | ### Integers
56 |
57 | The JS number type is always a double, and hence cannot accurately represent integers with more than 53 bits. As such, we've taken the route of a rarely used define (`LUA_INT_TYPE=LUA_INT_LONG`) in the PUC-Rio sources, where floats are doubles, but integers are 32 bits.
58 |
59 |
60 | ### `require` and `package.loadlib`
61 |
62 | In the browser `require` and `package.loadlib` try to find a file by making synchronous XHR requests.
63 |
64 | `require` has been extended to allow searchers to yield.
65 |
66 |
67 | ### _Missing_ features
68 |
69 | - `lua_gc`/`collectgarbage`: Fengari relies on the JS garbage collector and does not implement its own.
70 | - The following functions are only available in Node:
71 | - The entire `io` lib
72 | - `os.remove`
73 | - `os.rename`
74 | - `os.tmpname`
75 | - `os.execute`
76 | - `debug.debug()` doesn't work from web workers due to lack of a method to get synchronous user input
77 | - [Weak tables](http://www.lua.org/manual/5.3/manual.html#2.5.2)
78 | - `__gc` metamethods
79 |
80 |
81 | ### _Differences_
82 |
83 | - `package.jspath` instead of `package.cpath`
84 | - `LUA_JSPATH_DEFAULT` instead of `LUA_CPATH_DEFAULT` (and contains .js extensions rather than .so or .dll extensions)
85 | - `lua_tointegerx` and `lua_tonumberx` do not have out-parameters indicating conversion success. Instead, ``false`` is returned when conversion fails.
86 | - `luaL_execresult` takes an extra argument: an error object. The error object should have fields `status`, `signal` and `errno`.
87 | - `luaL_fileresult` takes an extra argument: an error object. The error object should have a field `errno`.
88 |
89 |
90 | ### Configuring
91 |
92 | Some luaconf options can be chosen at library load time. Fengari looks for `process.env.FENGARICONF` and if it exists, parses it as a JSON string.
93 |
94 |
95 | ## Extensions
96 |
97 | ### `dv = lua_todataview(L, idx)`
98 |
99 | Equivalent to `lua_tolstring` but returns a [`DataView`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) instead of a string.
100 |
101 |
102 | ### `lua_pushjsfunction(L, func)`
103 |
104 | Alias for `lua_pushcfunction`.
105 |
106 |
107 | ### `lua_pushjsclosure(L, func, n)`
108 |
109 | Alias for `lua_pushcclosure`.
110 |
111 |
112 | ### `lua_atnativeerror(L, func)`
113 |
114 | Sets a function to be called if a native JavaScript error is thrown across a lua pcall.
115 | The function will be run as if it were a message handler (see https://www.lua.org/manual/5.3/manual.html#2.3).
116 | The current message handler will be run after the native error handler returns.
117 |
118 |
119 | ### `b = lua_isproxy(p, L)`
120 |
121 | Returns a boolean `b` indicating whether `p` is a proxy (See `lua_toproxy`).
122 | If `L` is non-null, only returns `true` if `p` belongs to the same global state.
123 |
124 |
125 | ### `p = lua_toproxy(L, idx)`
126 |
127 | Returns a JavaScript object `p` that holds a reference to the lua value at the stack index `idx`.
128 | This object can be called with a lua_State to push the value onto that state's stack.
129 |
130 | This example would be an inefficient way to write `lua_pushvalue(L, 1)`:
131 |
132 | ```js
133 | var p = lua_toproxy(L, 1);
134 | p(L);
135 | ```
136 |
137 |
138 | ### `fengari` library
139 |
140 | A library containing metadata about the fengari release.
141 |
142 | - `AUTHORS`
143 | - `COPYRIGHT`
144 | - `RELEASE`
145 | - `VERSION`
146 | - `VERSION_MAJOR`
147 | - `VERSION_MINOR`
148 | - `VERSION_NUM`
149 | - `VERSION_RELEASE`
150 |
151 | This library is automatically loaded by `luaL_openlibs` into the global `"fengari"`.
152 |
153 |
154 | ## NYI
155 |
156 | - `io.input()`: partially implemented
157 | - `io.lines()`
158 | - `io.open()`
159 | - `io.output()`: partially implemented
160 | - `io.popen()`
161 | - `io.read()`
162 | - `io.tmpfile()`
163 | - `file:lines()`
164 | - `file:read()`
165 | - `file:setvbuf()`
166 | - `file:__gc()`
167 |
168 |
169 | ## References
170 |
171 | - [Source code for Lua 5.3](lua.org/source/5.3/)
172 | - [Lua 5.2 Bytecode and Virtual Machine](http://files.catwell.info/misc/mirror/lua-5.2-bytecode-vm-dirk-laurie/lua52vm.html)
173 | - [Lua 5.3 Bytecode Reference](http://the-ravi-programming-language.readthedocs.io/en/latest/lua_bytecode_reference.html)
174 | - [A No-Frills Introduction to Lua 5.1 VM Instructions](http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf)
175 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengari-lua/fengari/bd945f0ef42caad86aff59365fcb8a3ec8509d06/logo.png
--------------------------------------------------------------------------------
/logo.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengari-lua/fengari/bd945f0ef42caad86aff59365fcb8a3ec8509d06/logo.sketch
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fengari",
3 | "version": "0.1.4",
4 | "description": "A Lua VM written in JS ES6 targeting the browser",
5 | "main": "src/fengari.js",
6 | "directories": {
7 | "lib": "src",
8 | "test": "test"
9 | },
10 | "scripts": {
11 | "lint": "eslint src/ test/",
12 | "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run test",
13 | "test": "jest"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/fengari-lua/fengari.git"
18 | },
19 | "keywords": [
20 | "lua",
21 | "vm"
22 | ],
23 | "contributors": [
24 | "Benoit Giannangeli",
25 | "Daurnimator "
26 | ],
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/fengari-lua/fengari/issues"
30 | },
31 | "homepage": "https://github.com/fengari-lua/fengari#readme",
32 | "devDependencies": {
33 | "eslint": "^5.15.1",
34 | "jest": "^24.5.0"
35 | },
36 | "dependencies": {
37 | "readline-sync": "^1.4.9",
38 | "sprintf-js": "^1.1.2",
39 | "tmp": "^0.0.33"
40 | },
41 | "sideEffects": false,
42 | "eslintConfig": {
43 | "env": {
44 | "browser": true,
45 | "es6": true,
46 | "node": true,
47 | "worker": true
48 | },
49 | "extends": "eslint:recommended",
50 | "rules": {
51 | "indent": [
52 | "error",
53 | 4,
54 | {
55 | "SwitchCase": 1
56 | }
57 | ],
58 | "linebreak-style": [
59 | "error",
60 | "unix"
61 | ],
62 | "no-console": 0,
63 | "no-empty": [
64 | 2,
65 | {
66 | "allowEmptyCatch": true
67 | }
68 | ],
69 | "no-unused-vars": [
70 | 2,
71 | {
72 | "args": "none"
73 | }
74 | ],
75 | "semi": [
76 | "error",
77 | "always"
78 | ]
79 | },
80 | "overrides": [
81 | {
82 | "files": [
83 | "test/**/*.test.js"
84 | ],
85 | "env": {
86 | "jest": true
87 | }
88 | }
89 | ]
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/fengari.js:
--------------------------------------------------------------------------------
1 | /**
2 | @license MIT
3 |
4 | Copyright © 2017-2019 Benoit Giannangeli
5 | Copyright © 2017-2019 Daurnimator
6 | Copyright © 1994–2017 Lua.org, PUC-Rio.
7 | */
8 |
9 | "use strict";
10 |
11 | const core = require("./fengaricore.js");
12 |
13 | module.exports.FENGARI_AUTHORS = core.FENGARI_AUTHORS;
14 | module.exports.FENGARI_COPYRIGHT = core.FENGARI_COPYRIGHT;
15 | module.exports.FENGARI_RELEASE = core.FENGARI_RELEASE;
16 | module.exports.FENGARI_VERSION = core.FENGARI_VERSION;
17 | module.exports.FENGARI_VERSION_MAJOR = core.FENGARI_VERSION_MAJOR;
18 | module.exports.FENGARI_VERSION_MINOR = core.FENGARI_VERSION_MINOR;
19 | module.exports.FENGARI_VERSION_NUM = core.FENGARI_VERSION_NUM;
20 | module.exports.FENGARI_VERSION_RELEASE = core.FENGARI_VERSION_RELEASE;
21 |
22 | module.exports.luastring_eq = core.luastring_eq;
23 | module.exports.luastring_indexOf = core.luastring_indexOf;
24 | module.exports.luastring_of = core.luastring_of;
25 | module.exports.to_jsstring = core.to_jsstring;
26 | module.exports.to_luastring = core.to_luastring;
27 | module.exports.to_uristring = core.to_uristring;
28 |
29 | const luaconf = require('./luaconf.js');
30 | const lua = require('./lua.js');
31 | const lauxlib = require('./lauxlib.js');
32 | const lualib = require('./lualib.js');
33 |
34 | module.exports.luaconf = luaconf;
35 | module.exports.lua = lua;
36 | module.exports.lauxlib = lauxlib;
37 | module.exports.lualib = lualib;
38 |
--------------------------------------------------------------------------------
/src/fengaricore.js:
--------------------------------------------------------------------------------
1 | /* Fengari specific functions
2 | *
3 | * This file includes fengari-specific data or and functionality for users to
4 | * manipulate fengari's string type.
5 | * The fields are exposed to the user on the 'fengari' entry point; however to
6 | * avoid a dependency on defs.js from lauxlib.js they are defined in this file.
7 | */
8 |
9 | const defs = require("./defs.js");
10 |
11 | const FENGARI_VERSION_MAJOR = "0";
12 | const FENGARI_VERSION_MINOR = "1";
13 | const FENGARI_VERSION_NUM = 1;
14 | const FENGARI_VERSION_RELEASE = "4";
15 | const FENGARI_VERSION = "Fengari " + FENGARI_VERSION_MAJOR + "." + FENGARI_VERSION_MINOR;
16 | const FENGARI_RELEASE = FENGARI_VERSION + "." + FENGARI_VERSION_RELEASE;
17 | const FENGARI_AUTHORS = "B. Giannangeli, Daurnimator";
18 | const FENGARI_COPYRIGHT = FENGARI_RELEASE + " Copyright (C) 2017-2019 " + FENGARI_AUTHORS + "\nBased on: " + defs.LUA_COPYRIGHT;
19 |
20 | module.exports.FENGARI_AUTHORS = FENGARI_AUTHORS;
21 | module.exports.FENGARI_COPYRIGHT = FENGARI_COPYRIGHT;
22 | module.exports.FENGARI_RELEASE = FENGARI_RELEASE;
23 | module.exports.FENGARI_VERSION = FENGARI_VERSION;
24 | module.exports.FENGARI_VERSION_MAJOR = FENGARI_VERSION_MAJOR;
25 | module.exports.FENGARI_VERSION_MINOR = FENGARI_VERSION_MINOR;
26 | module.exports.FENGARI_VERSION_NUM = FENGARI_VERSION_NUM;
27 | module.exports.FENGARI_VERSION_RELEASE = FENGARI_VERSION_RELEASE;
28 | module.exports.is_luastring = defs.is_luastring;
29 | module.exports.luastring_eq = defs.luastring_eq;
30 | module.exports.luastring_from = defs.luastring_from;
31 | module.exports.luastring_indexOf = defs.luastring_indexOf;
32 | module.exports.luastring_of = defs.luastring_of;
33 | module.exports.to_jsstring = defs.to_jsstring;
34 | module.exports.to_luastring = defs.to_luastring;
35 | module.exports.to_uristring = defs.to_uristring;
36 | module.exports.from_userstring = defs.from_userstring;
37 |
--------------------------------------------------------------------------------
/src/fengarilib.js:
--------------------------------------------------------------------------------
1 | const {
2 | lua_pushinteger,
3 | lua_pushliteral,
4 | lua_setfield
5 | } = require('./lua.js');
6 | const {
7 | luaL_newlib
8 | } = require('./lauxlib.js');
9 | const {
10 | FENGARI_AUTHORS,
11 | FENGARI_COPYRIGHT,
12 | FENGARI_RELEASE,
13 | FENGARI_VERSION,
14 | FENGARI_VERSION_MAJOR,
15 | FENGARI_VERSION_MINOR,
16 | FENGARI_VERSION_NUM,
17 | FENGARI_VERSION_RELEASE,
18 | to_luastring
19 | } = require("./fengaricore.js");
20 |
21 | const luaopen_fengari = function(L) {
22 | luaL_newlib(L, {});
23 | lua_pushliteral(L, FENGARI_AUTHORS);
24 | lua_setfield(L, -2, to_luastring("AUTHORS"));
25 | lua_pushliteral(L, FENGARI_COPYRIGHT);
26 | lua_setfield(L, -2, to_luastring("COPYRIGHT"));
27 | lua_pushliteral(L, FENGARI_RELEASE);
28 | lua_setfield(L, -2, to_luastring("RELEASE"));
29 | lua_pushliteral(L, FENGARI_VERSION);
30 | lua_setfield(L, -2, to_luastring("VERSION"));
31 | lua_pushliteral(L, FENGARI_VERSION_MAJOR);
32 | lua_setfield(L, -2, to_luastring("VERSION_MAJOR"));
33 | lua_pushliteral(L, FENGARI_VERSION_MINOR);
34 | lua_setfield(L, -2, to_luastring("VERSION_MINOR"));
35 | lua_pushinteger(L, FENGARI_VERSION_NUM);
36 | lua_setfield(L, -2, to_luastring("VERSION_NUM"));
37 | lua_pushliteral(L, FENGARI_VERSION_RELEASE);
38 | lua_setfield(L, -2, to_luastring("VERSION_RELEASE"));
39 | return 1;
40 | };
41 |
42 | module.exports.luaopen_fengari = luaopen_fengari;
43 |
--------------------------------------------------------------------------------
/src/lcorolib.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | LUA_OK,
5 | LUA_TFUNCTION,
6 | LUA_TSTRING,
7 | LUA_YIELD,
8 | lua_Debug,
9 | lua_checkstack,
10 | lua_concat,
11 | lua_error,
12 | lua_getstack,
13 | lua_gettop,
14 | lua_insert,
15 | lua_isyieldable,
16 | lua_newthread,
17 | lua_pop,
18 | lua_pushboolean,
19 | lua_pushcclosure,
20 | lua_pushliteral,
21 | lua_pushthread,
22 | lua_pushvalue,
23 | lua_resume,
24 | lua_status,
25 | lua_tothread,
26 | lua_type,
27 | lua_upvalueindex,
28 | lua_xmove,
29 | lua_yield
30 | } = require('./lua.js');
31 | const {
32 | luaL_argcheck,
33 | luaL_checktype,
34 | luaL_newlib,
35 | luaL_where
36 | } = require('./lauxlib.js');
37 |
38 | const getco = function(L) {
39 | let co = lua_tothread(L, 1);
40 | luaL_argcheck(L, co, 1, "thread expected");
41 | return co;
42 | };
43 |
44 | const auxresume = function(L, co, narg) {
45 | if (!lua_checkstack(co, narg)) {
46 | lua_pushliteral(L, "too many arguments to resume");
47 | return -1; /* error flag */
48 | }
49 |
50 | if (lua_status(co) === LUA_OK && lua_gettop(co) === 0) {
51 | lua_pushliteral(L, "cannot resume dead coroutine");
52 | return -1; /* error flag */
53 | }
54 |
55 | lua_xmove(L, co, narg);
56 | let status = lua_resume(co, L, narg);
57 | if (status === LUA_OK || status === LUA_YIELD) {
58 | let nres = lua_gettop(co);
59 | if (!lua_checkstack(L, nres + 1)) {
60 | lua_pop(co, nres); /* remove results anyway */
61 | lua_pushliteral(L, "too many results to resume");
62 | return -1; /* error flag */
63 | }
64 |
65 | lua_xmove(co, L, nres); /* move yielded values */
66 | return nres;
67 | } else {
68 | lua_xmove(co, L, 1); /* move error message */
69 | return -1; /* error flag */
70 | }
71 | };
72 |
73 | const luaB_coresume = function(L) {
74 | let co = getco(L);
75 | let r = auxresume(L, co, lua_gettop(L) - 1);
76 | if (r < 0) {
77 | lua_pushboolean(L, 0);
78 | lua_insert(L, -2);
79 | return 2; /* return false + error message */
80 | } else {
81 | lua_pushboolean(L, 1);
82 | lua_insert(L, -(r + 1));
83 | return r + 1; /* return true + 'resume' returns */
84 | }
85 | };
86 |
87 | const luaB_auxwrap = function(L) {
88 | let co = lua_tothread(L, lua_upvalueindex(1));
89 | let r = auxresume(L, co, lua_gettop(L));
90 | if (r < 0) {
91 | if (lua_type(L, -1) === LUA_TSTRING) { /* error object is a string? */
92 | luaL_where(L, 1); /* add extra info */
93 | lua_insert(L, -2);
94 | lua_concat(L, 2);
95 | }
96 |
97 | return lua_error(L); /* propagate error */
98 | }
99 |
100 | return r;
101 | };
102 |
103 | const luaB_cocreate = function(L) {
104 | luaL_checktype(L, 1, LUA_TFUNCTION);
105 | let NL = lua_newthread(L);
106 | lua_pushvalue(L, 1); /* move function to top */
107 | lua_xmove(L, NL, 1); /* move function from L to NL */
108 | return 1;
109 | };
110 |
111 | const luaB_cowrap = function(L) {
112 | luaB_cocreate(L);
113 | lua_pushcclosure(L, luaB_auxwrap, 1);
114 | return 1;
115 | };
116 |
117 | const luaB_yield = function(L) {
118 | return lua_yield(L, lua_gettop(L));
119 | };
120 |
121 | const luaB_costatus = function(L) {
122 | let co = getco(L);
123 | if (L === co) lua_pushliteral(L, "running");
124 | else {
125 | switch (lua_status(co)) {
126 | case LUA_YIELD:
127 | lua_pushliteral(L, "suspended");
128 | break;
129 | case LUA_OK: {
130 | let ar = new lua_Debug();
131 | if (lua_getstack(co, 0, ar) > 0) /* does it have frames? */
132 | lua_pushliteral(L, "normal"); /* it is running */
133 | else if (lua_gettop(co) === 0)
134 | lua_pushliteral(L, "dead");
135 | else
136 | lua_pushliteral(L, "suspended"); /* initial state */
137 | break;
138 | }
139 | default: /* some error occurred */
140 | lua_pushliteral(L, "dead");
141 | break;
142 | }
143 | }
144 |
145 | return 1;
146 | };
147 |
148 | const luaB_yieldable = function(L) {
149 | lua_pushboolean(L, lua_isyieldable(L));
150 | return 1;
151 | };
152 |
153 | const luaB_corunning = function(L) {
154 | lua_pushboolean(L, lua_pushthread(L));
155 | return 2;
156 | };
157 |
158 | const co_funcs = {
159 | "create": luaB_cocreate,
160 | "isyieldable": luaB_yieldable,
161 | "resume": luaB_coresume,
162 | "running": luaB_corunning,
163 | "status": luaB_costatus,
164 | "wrap": luaB_cowrap,
165 | "yield": luaB_yield
166 | };
167 |
168 | const luaopen_coroutine = function(L) {
169 | luaL_newlib(L, co_funcs);
170 | return 1;
171 | };
172 |
173 | module.exports.luaopen_coroutine = luaopen_coroutine;
174 |
--------------------------------------------------------------------------------
/src/ldump.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | LUA_SIGNATURE,
5 | LUA_VERSION_MAJOR,
6 | LUA_VERSION_MINOR,
7 | constant_types: {
8 | LUA_TBOOLEAN,
9 | LUA_TLNGSTR,
10 | LUA_TNIL,
11 | LUA_TNUMFLT,
12 | LUA_TNUMINT,
13 | LUA_TSHRSTR
14 | },
15 | luastring_of
16 | } = require('./defs.js');
17 |
18 | const LUAC_DATA = luastring_of(25, 147, 13, 10, 26, 10);
19 | const LUAC_INT = 0x5678;
20 | const LUAC_NUM = 370.5;
21 | const LUAC_VERSION = Number(LUA_VERSION_MAJOR) * 16 + Number(LUA_VERSION_MINOR);
22 | const LUAC_FORMAT = 0; /* this is the official format */
23 |
24 | class DumpState {
25 | constructor() {
26 | this.L = null;
27 | this.writer = null;
28 | this.data = null;
29 | this.strip = NaN;
30 | this.status = NaN;
31 | }
32 | }
33 |
34 | const DumpBlock = function(b, size, D) {
35 | if (D.status === 0 && size > 0)
36 | D.status = D.writer(D.L, b, size, D.data);
37 | };
38 |
39 | const DumpByte = function(y, D) {
40 | DumpBlock(luastring_of(y), 1, D);
41 | };
42 |
43 | const DumpInt = function(x, D) {
44 | let ab = new ArrayBuffer(4);
45 | let dv = new DataView(ab);
46 | dv.setInt32(0, x, true);
47 | let t = new Uint8Array(ab);
48 | DumpBlock(t, 4, D);
49 | };
50 |
51 | const DumpInteger = function(x, D) {
52 | let ab = new ArrayBuffer(4);
53 | let dv = new DataView(ab);
54 | dv.setInt32(0, x, true);
55 | let t = new Uint8Array(ab);
56 | DumpBlock(t, 4, D);
57 | };
58 |
59 | const DumpNumber = function(x, D) {
60 | let ab = new ArrayBuffer(8);
61 | let dv = new DataView(ab);
62 | dv.setFloat64(0, x, true);
63 | let t = new Uint8Array(ab);
64 | DumpBlock(t, 8, D);
65 | };
66 |
67 | const DumpString = function(s, D) {
68 | if (s === null)
69 | DumpByte(0, D);
70 | else {
71 | let size = s.tsslen() + 1;
72 | let str = s.getstr();
73 | if (size < 0xFF)
74 | DumpByte(size, D);
75 | else {
76 | DumpByte(0xFF, D);
77 | DumpInteger(size, D);
78 | }
79 | DumpBlock(str, size - 1, D); /* no need to save '\0' */
80 | }
81 | };
82 |
83 | const DumpCode = function(f, D) {
84 | let s = f.code.map(e => e.code);
85 | DumpInt(s.length, D);
86 |
87 | for (let i = 0; i < s.length; i++)
88 | DumpInt(s[i], D);
89 | };
90 |
91 | const DumpConstants = function(f, D) {
92 | let n = f.k.length;
93 | DumpInt(n, D);
94 | for (let i = 0; i < n; i++) {
95 | let o = f.k[i];
96 | DumpByte(o.ttype(), D);
97 | switch (o.ttype()) {
98 | case LUA_TNIL:
99 | break;
100 | case LUA_TBOOLEAN:
101 | DumpByte(o.value ? 1 : 0, D);
102 | break;
103 | case LUA_TNUMFLT:
104 | DumpNumber(o.value, D);
105 | break;
106 | case LUA_TNUMINT:
107 | DumpInteger(o.value, D);
108 | break;
109 | case LUA_TSHRSTR:
110 | case LUA_TLNGSTR:
111 | DumpString(o.tsvalue(), D);
112 | break;
113 | }
114 | }
115 | };
116 |
117 | const DumpProtos = function(f, D) {
118 | let n = f.p.length;
119 | DumpInt(n, D);
120 | for (let i = 0; i < n; i++)
121 | DumpFunction(f.p[i], f.source, D);
122 | };
123 |
124 | const DumpUpvalues = function(f, D) {
125 | let n = f.upvalues.length;
126 | DumpInt(n, D);
127 | for (let i = 0; i < n; i++) {
128 | DumpByte(f.upvalues[i].instack ? 1 : 0, D);
129 | DumpByte(f.upvalues[i].idx, D);
130 | }
131 | };
132 |
133 | const DumpDebug = function(f, D) {
134 | let n = D.strip ? 0 : f.lineinfo.length;
135 | DumpInt(n, D);
136 | for (let i = 0; i < n; i++)
137 | DumpInt(f.lineinfo[i], D);
138 | n = D.strip ? 0 : f.locvars.length;
139 | DumpInt(n, D);
140 | for (let i = 0; i < n; i++) {
141 | DumpString(f.locvars[i].varname, D);
142 | DumpInt(f.locvars[i].startpc, D);
143 | DumpInt(f.locvars[i].endpc, D);
144 | }
145 | n = D.strip ? 0 : f.upvalues.length;
146 | DumpInt(n, D);
147 | for (let i = 0; i < n; i++)
148 | DumpString(f.upvalues[i].name, D);
149 | };
150 |
151 | const DumpFunction = function(f, psource, D) {
152 | if (D.strip || f.source === psource)
153 | DumpString(null, D); /* no debug info or same source as its parent */
154 | else
155 | DumpString(f.source, D);
156 | DumpInt(f.linedefined, D);
157 | DumpInt(f.lastlinedefined, D);
158 | DumpByte(f.numparams, D);
159 | DumpByte(f.is_vararg?1:0, D);
160 | DumpByte(f.maxstacksize, D);
161 | DumpCode(f, D);
162 | DumpConstants(f, D);
163 | DumpUpvalues(f, D);
164 | DumpProtos(f, D);
165 | DumpDebug(f, D);
166 | };
167 |
168 | const DumpHeader = function(D) {
169 | DumpBlock(LUA_SIGNATURE, LUA_SIGNATURE.length, D);
170 | DumpByte(LUAC_VERSION, D);
171 | DumpByte(LUAC_FORMAT, D);
172 | DumpBlock(LUAC_DATA, LUAC_DATA.length, D);
173 | DumpByte(4, D); // intSize
174 | DumpByte(4, D); // size_tSize
175 | DumpByte(4, D); // instructionSize
176 | DumpByte(4, D); // integerSize
177 | DumpByte(8, D); // numberSize
178 | DumpInteger(LUAC_INT, D);
179 | DumpNumber(LUAC_NUM, D);
180 | };
181 |
182 | /*
183 | ** dump Lua function as precompiled chunk
184 | */
185 | const luaU_dump = function(L, f, w, data, strip) {
186 | let D = new DumpState();
187 | D.L = L;
188 | D.writer = w;
189 | D.data = data;
190 | D.strip = strip;
191 | D.status = 0;
192 | DumpHeader(D);
193 | DumpByte(f.upvalues.length, D);
194 | DumpFunction(f, null, D);
195 | return D.status;
196 | };
197 |
198 | module.exports.luaU_dump = luaU_dump;
199 |
--------------------------------------------------------------------------------
/src/lfunc.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { constant_types: { LUA_TNIL } } = require('./defs.js');
4 | const lobject = require('./lobject.js');
5 |
6 | class Proto {
7 | constructor(L) {
8 | this.id = L.l_G.id_counter++;
9 | this.k = []; // constants used by the function
10 | this.p = []; // functions defined inside the function
11 | this.code = []; // opcodes
12 | this.cache = null; // last-created closure with this prototype
13 | this.lineinfo = []; // map from opcodes to source lines (debug information)
14 | this.upvalues = []; // upvalue information
15 | this.numparams = 0; // number of fixed parameters
16 | this.is_vararg = false;
17 | this.maxstacksize = 0; // number of registers needed by this function
18 | this.locvars = []; // information about local variables (debug information)
19 | this.linedefined = 0; // debug information
20 | this.lastlinedefined = 0; // debug information
21 | this.source = null; // used for debug information
22 | }
23 | }
24 |
25 | const luaF_newLclosure = function(L, n) {
26 | return new lobject.LClosure(L, n);
27 | };
28 |
29 |
30 | const luaF_findupval = function(L, level) {
31 | return L.stack[level];
32 | };
33 |
34 | const luaF_close = function(L, level) {
35 | /* Create new TValues on stack;
36 | * any closures will keep referencing old TValues */
37 | for (let i=level; i 6 ?
135 | (s) => s : // identity function
136 | (s) => Buffer.from(s.buffer, s.byteOffset, s.byteLength);
137 |
138 | const g_write = function(L, f, arg) {
139 | let nargs = lua_gettop(L) - arg;
140 | let status = true;
141 | let err;
142 | for (; nargs--; arg++) {
143 | let s = luaL_checklstring(L, arg);
144 | try {
145 | status = status && (fs.writeSync(f.fd, prepare_string_for_write(s), 0, s.length) === s.length);
146 | } catch (e) {
147 | status = false;
148 | err = e;
149 | }
150 | }
151 | if (status) return 1; /* file handle already on stack top */
152 | else return luaL_fileresult(L, status, null, err);
153 | };
154 |
155 | const io_write = function(L) {
156 | return g_write(L, getiofile(L, IO_OUTPUT), 1);
157 | };
158 |
159 | const f_write = function(L) {
160 | let f = tofile(L);
161 | lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
162 | return g_write(L, f, 2);
163 | };
164 |
165 | const io_flush = function (L) {
166 | /* stub, as node doesn't have synchronized buffered IO */
167 | getiofile(L, IO_OUTPUT);
168 | return luaL_fileresult(L, true, null, null);
169 | };
170 |
171 | const f_flush = function (L) {
172 | /* stub, as node doesn't have synchronized buffered IO */
173 | tofile(L);
174 | return luaL_fileresult(L, true, null, null);
175 | };
176 |
177 | const iolib = {
178 | "close": io_close,
179 | "flush": io_flush,
180 | "input": io_input,
181 | "output": io_output,
182 | "type": io_type,
183 | "write": io_write
184 | };
185 |
186 | const flib = {
187 | "close": io_close,
188 | "flush": f_flush,
189 | "write": f_write,
190 | "__tostring": f_tostring
191 | };
192 |
193 | const createmeta = function(L) {
194 | luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
195 | lua_pushvalue(L, -1); /* push metatable */
196 | lua_setfield(L, -2, to_luastring("__index", true)); /* metatable.__index = metatable */
197 | luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
198 | lua_pop(L, 1); /* pop new metatable */
199 | };
200 |
201 | const io_noclose = function(L) {
202 | let p = tolstream(L);
203 | p.closef = io_noclose;
204 | lua_pushnil(L);
205 | lua_pushliteral(L, "cannot close standard file");
206 | return 2;
207 | };
208 |
209 | const createstdfile = function(L, f, k, fname) {
210 | let p = newprefile(L);
211 | p.f = f;
212 | p.closef = io_noclose;
213 | if (k !== null) {
214 | lua_pushvalue(L, -1);
215 | lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
216 | }
217 | lua_setfield(L, -2, fname); /* add file to module */
218 | };
219 |
220 | const luaopen_io = function(L) {
221 | luaL_newlib(L, iolib);
222 | createmeta(L);
223 | /* create (and set) default files */
224 | createstdfile(L, process.stdin, IO_INPUT, to_luastring("stdin"));
225 | createstdfile(L, process.stdout, IO_OUTPUT, to_luastring("stdout"));
226 | createstdfile(L, process.stderr, null, to_luastring("stderr"));
227 | return 1;
228 | };
229 |
230 | module.exports.luaopen_io = luaopen_io;
231 |
--------------------------------------------------------------------------------
/src/ljstype.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { luastring_of } = require('./defs.js');
4 |
5 | const luai_ctype_ = luastring_of(
6 | 0x00, /* EOZ */
7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
8 | 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
9 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11 | 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
12 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
13 | 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
14 | 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
15 | 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
16 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
17 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
18 | 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
19 | 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
20 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
21 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
22 | 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */
24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */
26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */
28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */
30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */
32 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */
34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */
36 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */
38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
39 | );
40 |
41 | const ALPHABIT = 0;
42 | const DIGITBIT = 1;
43 | const PRINTBIT = 2;
44 | const SPACEBIT = 3;
45 | const XDIGITBIT = 4;
46 |
47 | const lisdigit = function(c) {
48 | return (luai_ctype_[c+1] & (1<= 0 || up <= LUA_MAXINTEGER + low, 1,
75 | "interval too large");
76 |
77 | r *= (up - low) + 1;
78 | lua_pushinteger(L, Math.floor(r) + low);
79 | return 1;
80 | };
81 |
82 | const math_randomseed = function(L) {
83 | l_srand(luaL_checknumber(L, 1));
84 | l_rand(); /* discard first value to avoid undesirable correlations */
85 | return 0;
86 | };
87 |
88 | const math_abs = function(L) {
89 | if (lua_isinteger(L, 1)) {
90 | let n = lua_tointeger(L, 1);
91 | if (n < 0) n = (-n)|0;
92 | lua_pushinteger(L, n);
93 | }
94 | else
95 | lua_pushnumber(L, Math.abs(luaL_checknumber(L, 1)));
96 | return 1;
97 | };
98 |
99 | const math_sin = function(L) {
100 | lua_pushnumber(L, Math.sin(luaL_checknumber(L, 1)));
101 | return 1;
102 | };
103 |
104 | const math_cos = function(L) {
105 | lua_pushnumber(L, Math.cos(luaL_checknumber(L, 1)));
106 | return 1;
107 | };
108 |
109 | const math_tan = function(L) {
110 | lua_pushnumber(L, Math.tan(luaL_checknumber(L, 1)));
111 | return 1;
112 | };
113 |
114 | const math_asin = function(L) {
115 | lua_pushnumber(L, Math.asin(luaL_checknumber(L, 1)));
116 | return 1;
117 | };
118 |
119 | const math_acos = function(L) {
120 | lua_pushnumber(L, Math.acos(luaL_checknumber(L, 1)));
121 | return 1;
122 | };
123 |
124 | const math_atan = function(L) {
125 | let y = luaL_checknumber(L, 1);
126 | let x = luaL_optnumber(L, 2, 1);
127 | lua_pushnumber(L, Math.atan2(y, x));
128 | return 1;
129 | };
130 |
131 | const math_toint = function(L) {
132 | let n = lua_tointegerx(L, 1);
133 | if (n !== false)
134 | lua_pushinteger(L, n);
135 | else {
136 | luaL_checkany(L, 1);
137 | lua_pushnil(L); /* value is not convertible to integer */
138 | }
139 | return 1;
140 | };
141 |
142 | const pushnumint = function(L, d) {
143 | let n = lua_numbertointeger(d);
144 | if (n !== false) /* does 'd' fit in an integer? */
145 | lua_pushinteger(L, n); /* result is integer */
146 | else
147 | lua_pushnumber(L, d); /* result is float */
148 | };
149 |
150 | const math_floor = function(L) {
151 | if (lua_isinteger(L, 1))
152 | lua_settop(L, 1);
153 | else
154 | pushnumint(L, Math.floor(luaL_checknumber(L, 1)));
155 |
156 | return 1;
157 | };
158 |
159 | const math_ceil = function(L) {
160 | if (lua_isinteger(L, 1))
161 | lua_settop(L, 1);
162 | else
163 | pushnumint(L, Math.ceil(luaL_checknumber(L, 1)));
164 |
165 | return 1;
166 | };
167 |
168 | const math_sqrt = function(L) {
169 | lua_pushnumber(L, Math.sqrt(luaL_checknumber(L, 1)));
170 | return 1;
171 | };
172 |
173 | const math_ult = function(L) {
174 | let a = luaL_checkinteger(L, 1);
175 | let b = luaL_checkinteger(L, 2);
176 | lua_pushboolean(L, (a >= 0)?(b<0 || a= 1, 1, "value expected");
217 | for (let i = 2; i <= n; i++){
218 | if (lua_compare(L, i, imin, LUA_OPLT))
219 | imin = i;
220 | }
221 | lua_pushvalue(L, imin);
222 | return 1;
223 | };
224 |
225 | const math_max = function(L) {
226 | let n = lua_gettop(L); /* number of arguments */
227 | let imax = 1; /* index of current minimum value */
228 | luaL_argcheck(L, n >= 1, 1, "value expected");
229 | for (let i = 2; i <= n; i++){
230 | if (lua_compare(L, imax, i, LUA_OPLT))
231 | imax = i;
232 | }
233 | lua_pushvalue(L, imax);
234 | return 1;
235 | };
236 |
237 | const math_type = function(L) {
238 | if (lua_type(L, 1) === LUA_TNUMBER) {
239 | if (lua_isinteger(L, 1))
240 | lua_pushliteral(L, "integer");
241 | else
242 | lua_pushliteral(L, "float");
243 | } else {
244 | luaL_checkany(L, 1);
245 | lua_pushnil(L);
246 | }
247 | return 1;
248 | };
249 |
250 | const math_fmod = function(L) {
251 | if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) {
252 | let d = lua_tointeger(L, 2);
253 | /* no special case needed for -1 in javascript */
254 | if (d === 0) {
255 | luaL_argerror(L, 2, "zero");
256 | } else
257 | lua_pushinteger(L, (lua_tointeger(L, 1) % d)|0);
258 | } else {
259 | let a = luaL_checknumber(L, 1);
260 | let b = luaL_checknumber(L, 2);
261 | lua_pushnumber(L, a%b);
262 | }
263 | return 1;
264 | };
265 |
266 | const math_modf = function(L) {
267 | if (lua_isinteger(L, 1)) {
268 | lua_settop(L, 1); /* number is its own integer part */
269 | lua_pushnumber(L, 0); /* no fractional part */
270 | } else {
271 | let n = luaL_checknumber(L, 1);
272 | let ip = n < 0 ? Math.ceil(n) : Math.floor(n);
273 | pushnumint(L, ip);
274 | lua_pushnumber(L, n === ip ? 0 : n - ip);
275 | }
276 | return 2;
277 | };
278 |
279 | const mathlib = {
280 | "abs": math_abs,
281 | "acos": math_acos,
282 | "asin": math_asin,
283 | "atan": math_atan,
284 | "ceil": math_ceil,
285 | "cos": math_cos,
286 | "deg": math_deg,
287 | "exp": math_exp,
288 | "floor": math_floor,
289 | "fmod": math_fmod,
290 | "log": math_log,
291 | "max": math_max,
292 | "min": math_min,
293 | "modf": math_modf,
294 | "rad": math_rad,
295 | "random": math_random,
296 | "randomseed": math_randomseed,
297 | "sin": math_sin,
298 | "sqrt": math_sqrt,
299 | "tan": math_tan,
300 | "tointeger": math_toint,
301 | "type": math_type,
302 | "ult": math_ult
303 | };
304 |
305 | const luaopen_math = function(L) {
306 | luaL_newlib(L, mathlib);
307 | lua_pushnumber(L, Math.PI);
308 | lua_setfield(L, -2, to_luastring("pi", true));
309 | lua_pushnumber(L, Infinity);
310 | lua_setfield(L, -2, to_luastring("huge", true));
311 | lua_pushinteger(L, LUA_MAXINTEGER);
312 | lua_setfield(L, -2, to_luastring("maxinteger", true));
313 | lua_pushinteger(L, LUA_MININTEGER);
314 | lua_setfield(L, -2, to_luastring("mininteger", true));
315 | return 1;
316 | };
317 |
318 | module.exports.luaopen_math = luaopen_math;
319 |
--------------------------------------------------------------------------------
/src/lstate.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | LUA_MINSTACK,
5 | LUA_RIDX_GLOBALS,
6 | LUA_RIDX_MAINTHREAD,
7 | constant_types: {
8 | LUA_NUMTAGS,
9 | LUA_TNIL,
10 | LUA_TTABLE,
11 | LUA_TTHREAD
12 | },
13 | thread_status: {
14 | LUA_OK
15 | }
16 | } = require('./defs.js');
17 | const lobject = require('./lobject.js');
18 | const ldo = require('./ldo.js');
19 | const lapi = require('./lapi.js');
20 | const ltable = require('./ltable.js');
21 | const ltm = require('./ltm.js');
22 |
23 | const EXTRA_STACK = 5;
24 |
25 | const BASIC_STACK_SIZE = 2 * LUA_MINSTACK;
26 |
27 | class CallInfo {
28 |
29 | constructor() {
30 | this.func = null;
31 | this.funcOff = NaN;
32 | this.top = NaN;
33 | this.previous = null;
34 | this.next = null;
35 |
36 | /* only for Lua functions */
37 | this.l_base = NaN; /* base for this function */
38 | this.l_code = null; /* reference to this.func.p.code */
39 | this.l_savedpc = NaN; /* offset into l_code */
40 | /* only for JS functions */
41 | this.c_k = null; /* continuation in case of yields */
42 | this.c_old_errfunc = null;
43 | this.c_ctx = null; /* context info. in case of yields */
44 |
45 | this.nresults = NaN;
46 | this.callstatus = NaN;
47 | }
48 |
49 | }
50 |
51 | class lua_State {
52 |
53 | constructor(g) {
54 | this.id = g.id_counter++;
55 |
56 | this.base_ci = new CallInfo(); /* CallInfo for first level (C calling Lua) */
57 | this.top = NaN; /* first free slot in the stack */
58 | this.stack_last = NaN; /* last free slot in the stack */
59 | this.oldpc = NaN; /* last pc traced */
60 |
61 | /* preinit_thread */
62 | this.l_G = g;
63 | this.stack = null;
64 | this.ci = null;
65 | this.errorJmp = null;
66 | this.nCcalls = 0;
67 | this.hook = null;
68 | this.hookmask = 0;
69 | this.basehookcount = 0;
70 | this.allowhook = 1;
71 | this.hookcount = this.basehookcount;
72 | this.nny = 1;
73 | this.status = LUA_OK;
74 | this.errfunc = 0;
75 | }
76 |
77 | }
78 |
79 | class global_State {
80 |
81 | constructor() {
82 | this.id_counter = 1; /* used to give objects unique ids */
83 | this.ids = new WeakMap();
84 |
85 | this.mainthread = null;
86 | this.l_registry = new lobject.TValue(LUA_TNIL, null);
87 | this.panic = null;
88 | this.atnativeerror = null;
89 | this.version = null;
90 | this.tmname = new Array(ltm.TMS.TM_N);
91 | this.mt = new Array(LUA_NUMTAGS);
92 | }
93 |
94 | }
95 |
96 | const luaE_extendCI = function(L) {
97 | let ci = new CallInfo();
98 | L.ci.next = ci;
99 | ci.previous = L.ci;
100 | ci.next = null;
101 | L.ci = ci;
102 | return ci;
103 | };
104 |
105 | const luaE_freeCI = function(L) {
106 | let ci = L.ci;
107 | ci.next = null;
108 | };
109 |
110 | const stack_init = function(L1, L) {
111 | L1.stack = new Array(BASIC_STACK_SIZE);
112 | L1.top = 0;
113 | L1.stack_last = BASIC_STACK_SIZE - EXTRA_STACK;
114 | /* initialize first ci */
115 | let ci = L1.base_ci;
116 | ci.next = ci.previous = null;
117 | ci.callstatus = 0;
118 | ci.funcOff = L1.top;
119 | ci.func = L1.stack[L1.top];
120 | L1.stack[L1.top++] = new lobject.TValue(LUA_TNIL, null);
121 | ci.top = L1.top + LUA_MINSTACK;
122 | L1.ci = ci;
123 | };
124 |
125 | const freestack = function(L) {
126 | L.ci = L.base_ci;
127 | luaE_freeCI(L);
128 | L.stack = null;
129 | };
130 |
131 | /*
132 | ** Create registry table and its predefined values
133 | */
134 | const init_registry = function(L, g) {
135 | let registry = ltable.luaH_new(L);
136 | g.l_registry.sethvalue(registry);
137 | ltable.luaH_setint(registry, LUA_RIDX_MAINTHREAD, new lobject.TValue(LUA_TTHREAD, L));
138 | ltable.luaH_setint(registry, LUA_RIDX_GLOBALS, new lobject.TValue(LUA_TTABLE, ltable.luaH_new(L)));
139 | };
140 |
141 | /*
142 | ** open parts of the state that may cause memory-allocation errors.
143 | ** ('g->version' !== NULL flags that the state was completely build)
144 | */
145 | const f_luaopen = function(L) {
146 | let g = L.l_G;
147 | stack_init(L, L);
148 | init_registry(L, g);
149 | ltm.luaT_init(L);
150 | g.version = lapi.lua_version(null);
151 | };
152 |
153 | const lua_newthread = function(L) {
154 | let g = L.l_G;
155 | let L1 = new lua_State(g);
156 | L.stack[L.top] = new lobject.TValue(LUA_TTHREAD, L1);
157 | lapi.api_incr_top(L);
158 | L1.hookmask = L.hookmask;
159 | L1.basehookcount = L.basehookcount;
160 | L1.hook = L.hook;
161 | L1.hookcount = L1.basehookcount;
162 | stack_init(L1, L);
163 | return L1;
164 | };
165 |
166 | const luaE_freethread = function(L, L1) {
167 | freestack(L1);
168 | };
169 |
170 | const lua_newstate = function() {
171 | let g = new global_State();
172 | let L = new lua_State(g);
173 | g.mainthread = L;
174 |
175 | if (ldo.luaD_rawrunprotected(L, f_luaopen, null) !== LUA_OK) {
176 | L = null;
177 | }
178 |
179 | return L;
180 | };
181 |
182 | const close_state = function(L) {
183 | freestack(L);
184 | };
185 |
186 | const lua_close = function(L) {
187 | L = L.l_G.mainthread; /* only the main thread can be closed */
188 | close_state(L);
189 | };
190 |
191 | module.exports.lua_State = lua_State;
192 | module.exports.CallInfo = CallInfo;
193 | module.exports.CIST_OAH = (1<<0); /* original value of 'allowhook' */
194 | module.exports.CIST_LUA = (1<<1); /* call is running a Lua function */
195 | module.exports.CIST_HOOKED = (1<<2); /* call is running a debug hook */
196 | module.exports.CIST_FRESH = (1<<3); /* call is running on a fresh invocation of luaV_execute */
197 | module.exports.CIST_YPCALL = (1<<4); /* call is a yieldable protected call */
198 | module.exports.CIST_TAIL = (1<<5); /* call was tail called */
199 | module.exports.CIST_HOOKYIELD = (1<<6); /* last hook called yielded */
200 | module.exports.CIST_LEQ = (1<<7); /* using __lt for __le */
201 | module.exports.CIST_FIN = (1<<8); /* call is running a finalizer */
202 | module.exports.EXTRA_STACK = EXTRA_STACK;
203 | module.exports.lua_close = lua_close;
204 | module.exports.lua_newstate = lua_newstate;
205 | module.exports.lua_newthread = lua_newthread;
206 | module.exports.luaE_extendCI = luaE_extendCI;
207 | module.exports.luaE_freeCI = luaE_freeCI;
208 | module.exports.luaE_freethread = luaE_freethread;
209 |
--------------------------------------------------------------------------------
/src/lstring.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | is_luastring,
5 | luastring_eq,
6 | luastring_from,
7 | to_luastring
8 | } = require('./defs.js');
9 | const { lua_assert } = require("./llimits.js");
10 |
11 | class TString {
12 |
13 | constructor(L, str) {
14 | this.hash = null;
15 | this.realstring = str;
16 | }
17 |
18 | getstr() {
19 | return this.realstring;
20 | }
21 |
22 | tsslen() {
23 | return this.realstring.length;
24 | }
25 |
26 | }
27 |
28 | const luaS_eqlngstr = function(a, b) {
29 | lua_assert(a instanceof TString);
30 | lua_assert(b instanceof TString);
31 | return a == b || luastring_eq(a.realstring, b.realstring);
32 | };
33 |
34 | /* converts strings (arrays) to a consistent map key
35 | make sure this doesn't conflict with any of the anti-collision strategies in ltable */
36 | const luaS_hash = function(str) {
37 | lua_assert(is_luastring(str));
38 | let len = str.length;
39 | let s = "|";
40 | for (let i=0; i LUA_MAXINTEGER / 2) { /* overflow? */
245 | /* table was built with bad purposes: resort to linear search */
246 | i = 1;
247 | while (!luaH_getint(t, i).ttisnil()) i++;
248 | return i - 1;
249 | }
250 | j *= 2;
251 | }
252 | /* now do a binary search between them */
253 | while (j - i > 1) {
254 | let m = Math.floor((i+j)/2);
255 | if (luaH_getint(t, m).ttisnil()) j = m;
256 | else i = m;
257 | }
258 | return i;
259 | };
260 |
261 | const luaH_next = function(L, table, keyI) {
262 | let keyO = L.stack[keyI];
263 |
264 | let entry;
265 | if (keyO.type === LUA_TNIL) {
266 | entry = table.f;
267 | if (!entry)
268 | return false;
269 | } else {
270 | /* First find current key */
271 | let hash = table_hash(L, keyO);
272 | /* Look in main part of table */
273 | entry = table.strong.get(hash);
274 | if (entry) {
275 | entry = entry.n;
276 | if (!entry)
277 | return false;
278 | } else {
279 | /* Try dead keys */
280 | entry = (table.dead_weak && table.dead_weak.get(hash)) || table.dead_strong.get(hash);
281 | if (!entry)
282 | /* item not in table */
283 | return ldebug.luaG_runerror(L, to_luastring("invalid key to 'next'"));
284 | /* Iterate until either out of keys, or until finding a non-dead key */
285 | do {
286 | entry = entry.n;
287 | if (!entry)
288 | return false;
289 | } while (entry.key.ttisdeadkey());
290 | }
291 | }
292 | lobject.setobj2s(L, keyI, entry.key);
293 | lobject.setobj2s(L, keyI+1, entry.value);
294 | return true;
295 | };
296 |
297 | module.exports.invalidateTMcache = invalidateTMcache;
298 | module.exports.luaH_get = luaH_get;
299 | module.exports.luaH_getint = luaH_getint;
300 | module.exports.luaH_getn = luaH_getn;
301 | module.exports.luaH_getstr = luaH_getstr;
302 | module.exports.luaH_setfrom = luaH_setfrom;
303 | module.exports.luaH_setint = luaH_setint;
304 | module.exports.luaH_new = luaH_new;
305 | module.exports.luaH_next = luaH_next;
306 | module.exports.Table = Table;
307 |
--------------------------------------------------------------------------------
/src/ltm.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | constant_types: {
5 | LUA_TTABLE,
6 | LUA_TUSERDATA
7 | },
8 | to_luastring
9 | } = require('./defs.js');
10 | const { lua_assert } = require('./llimits.js');
11 | const lobject = require('./lobject.js');
12 | const ldo = require('./ldo.js');
13 | const lstate = require('./lstate.js');
14 | const {
15 | luaS_bless,
16 | luaS_new
17 | } = require('./lstring.js');
18 | const ltable = require('./ltable.js');
19 | const ldebug = require('./ldebug.js');
20 | const lvm = require('./lvm.js');
21 |
22 | const luaT_typenames_ = [
23 | "no value",
24 | "nil",
25 | "boolean",
26 | "userdata",
27 | "number",
28 | "string",
29 | "table",
30 | "function",
31 | "userdata",
32 | "thread",
33 | "proto" /* this last case is used for tests only */
34 | ].map(e => to_luastring(e));
35 |
36 | const ttypename = function(t) {
37 | return luaT_typenames_[t + 1];
38 | };
39 |
40 |
41 | /*
42 | * WARNING: if you change the order of this enumeration,
43 | * grep "ORDER TM" and "ORDER OP"
44 | */
45 | const TMS = {
46 | TM_INDEX: 0,
47 | TM_NEWINDEX: 1,
48 | TM_GC: 2,
49 | TM_MODE: 3,
50 | TM_LEN: 4,
51 | TM_EQ: 5, /* last tag method with fast access */
52 | TM_ADD: 6,
53 | TM_SUB: 7,
54 | TM_MUL: 8,
55 | TM_MOD: 9,
56 | TM_POW: 10,
57 | TM_DIV: 11,
58 | TM_IDIV: 12,
59 | TM_BAND: 13 ,
60 | TM_BOR: 14,
61 | TM_BXOR: 15,
62 | TM_SHL: 16,
63 | TM_SHR: 17,
64 | TM_UNM: 18,
65 | TM_BNOT: 19,
66 | TM_LT: 20,
67 | TM_LE: 21,
68 | TM_CONCAT: 22,
69 | TM_CALL: 23,
70 | TM_N: 24 /* number of elements in the enum */
71 | };
72 |
73 | const luaT_init = function(L) {
74 | L.l_G.tmname[TMS.TM_INDEX] = new luaS_new(L, to_luastring("__index", true));
75 | L.l_G.tmname[TMS.TM_NEWINDEX] = new luaS_new(L, to_luastring("__newindex", true));
76 | L.l_G.tmname[TMS.TM_GC] = new luaS_new(L, to_luastring("__gc", true));
77 | L.l_G.tmname[TMS.TM_MODE] = new luaS_new(L, to_luastring("__mode", true));
78 | L.l_G.tmname[TMS.TM_LEN] = new luaS_new(L, to_luastring("__len", true));
79 | L.l_G.tmname[TMS.TM_EQ] = new luaS_new(L, to_luastring("__eq", true));
80 | L.l_G.tmname[TMS.TM_ADD] = new luaS_new(L, to_luastring("__add", true));
81 | L.l_G.tmname[TMS.TM_SUB] = new luaS_new(L, to_luastring("__sub", true));
82 | L.l_G.tmname[TMS.TM_MUL] = new luaS_new(L, to_luastring("__mul", true));
83 | L.l_G.tmname[TMS.TM_MOD] = new luaS_new(L, to_luastring("__mod", true));
84 | L.l_G.tmname[TMS.TM_POW] = new luaS_new(L, to_luastring("__pow", true));
85 | L.l_G.tmname[TMS.TM_DIV] = new luaS_new(L, to_luastring("__div", true));
86 | L.l_G.tmname[TMS.TM_IDIV] = new luaS_new(L, to_luastring("__idiv", true));
87 | L.l_G.tmname[TMS.TM_BAND] = new luaS_new(L, to_luastring("__band", true));
88 | L.l_G.tmname[TMS.TM_BOR] = new luaS_new(L, to_luastring("__bor", true));
89 | L.l_G.tmname[TMS.TM_BXOR] = new luaS_new(L, to_luastring("__bxor", true));
90 | L.l_G.tmname[TMS.TM_SHL] = new luaS_new(L, to_luastring("__shl", true));
91 | L.l_G.tmname[TMS.TM_SHR] = new luaS_new(L, to_luastring("__shr", true));
92 | L.l_G.tmname[TMS.TM_UNM] = new luaS_new(L, to_luastring("__unm", true));
93 | L.l_G.tmname[TMS.TM_BNOT] = new luaS_new(L, to_luastring("__bnot", true));
94 | L.l_G.tmname[TMS.TM_LT] = new luaS_new(L, to_luastring("__lt", true));
95 | L.l_G.tmname[TMS.TM_LE] = new luaS_new(L, to_luastring("__le", true));
96 | L.l_G.tmname[TMS.TM_CONCAT] = new luaS_new(L, to_luastring("__concat", true));
97 | L.l_G.tmname[TMS.TM_CALL] = new luaS_new(L, to_luastring("__call", true));
98 | };
99 |
100 | /*
101 | ** Return the name of the type of an object. For tables and userdata
102 | ** with metatable, use their '__name' metafield, if present.
103 | */
104 | const __name = to_luastring('__name', true);
105 | const luaT_objtypename = function(L, o) {
106 | let mt;
107 | if ((o.ttistable() && (mt = o.value.metatable) !== null) ||
108 | (o.ttisfulluserdata() && (mt = o.value.metatable) !== null)) {
109 | let name = ltable.luaH_getstr(mt, luaS_bless(L, __name));
110 | if (name.ttisstring())
111 | return name.svalue();
112 | }
113 | return ttypename(o.ttnov());
114 | };
115 |
116 | const luaT_callTM = function(L, f, p1, p2, p3, hasres) {
117 | let func = L.top;
118 |
119 | lobject.pushobj2s(L, f); /* push function (assume EXTRA_STACK) */
120 | lobject.pushobj2s(L, p1); /* 1st argument */
121 | lobject.pushobj2s(L, p2); /* 2nd argument */
122 |
123 | if (!hasres) /* no result? 'p3' is third argument */
124 | lobject.pushobj2s(L, p3); /* 3rd argument */
125 |
126 | if (L.ci.callstatus & lstate.CIST_LUA)
127 | ldo.luaD_call(L, func, hasres);
128 | else
129 | ldo.luaD_callnoyield(L, func, hasres);
130 |
131 | if (hasres) { /* if has result, move it to its place */
132 | let tv = L.stack[L.top-1];
133 | delete L.stack[--L.top];
134 | p3.setfrom(tv);
135 | }
136 | };
137 |
138 | const luaT_callbinTM = function(L, p1, p2, res, event) {
139 | let tm = luaT_gettmbyobj(L, p1, event);
140 | if (tm.ttisnil())
141 | tm = luaT_gettmbyobj(L, p2, event);
142 | if (tm.ttisnil()) return false;
143 | luaT_callTM(L, tm, p1, p2, res, 1);
144 | return true;
145 | };
146 |
147 | const luaT_trybinTM = function(L, p1, p2, res, event) {
148 | if (!luaT_callbinTM(L, p1, p2, res, event)) {
149 | switch (event) {
150 | case TMS.TM_CONCAT:
151 | return ldebug.luaG_concaterror(L, p1, p2);
152 | case TMS.TM_BAND: case TMS.TM_BOR: case TMS.TM_BXOR:
153 | case TMS.TM_SHL: case TMS.TM_SHR: case TMS.TM_BNOT: {
154 | let n1 = lvm.tonumber(p1);
155 | let n2 = lvm.tonumber(p2);
156 | if (n1 !== false && n2 !== false)
157 | return ldebug.luaG_tointerror(L, p1, p2);
158 | else
159 | return ldebug.luaG_opinterror(L, p1, p2, to_luastring("perform bitwise operation on", true));
160 | }
161 | default:
162 | return ldebug.luaG_opinterror(L, p1, p2, to_luastring("perform arithmetic on", true));
163 | }
164 | }
165 | };
166 |
167 | const luaT_callorderTM = function(L, p1, p2, event) {
168 | let res = new lobject.TValue();
169 | if (!luaT_callbinTM(L, p1, p2, res, event))
170 | return null;
171 | else
172 | return !res.l_isfalse();
173 | };
174 |
175 | const fasttm = function(l, et, e) {
176 | return et === null ? null :
177 | (et.flags & (1 << e)) ? null : luaT_gettm(et, e, l.l_G.tmname[e]);
178 | };
179 |
180 | const luaT_gettm = function(events, event, ename) {
181 | const tm = ltable.luaH_getstr(events, ename);
182 | lua_assert(event <= TMS.TM_EQ);
183 | if (tm.ttisnil()) { /* no tag method? */
184 | events.flags |= 1<= LUA_MININTEGER && n < -LUA_MININTEGER ? n : false;
159 | };
160 |
161 | const LUA_INTEGER_FRMLEN = "";
162 | const LUA_NUMBER_FRMLEN = "";
163 |
164 | const LUA_INTEGER_FMT = `%${LUA_INTEGER_FRMLEN}d`;
165 | const LUA_NUMBER_FMT = "%.14g";
166 |
167 | const lua_getlocaledecpoint = function() {
168 | /* we hard-code the decimal point to '.' as a user cannot change the
169 | locale in most JS environments, and in that you can, a multi-byte
170 | locale is common.
171 | */
172 | return 46 /* '.'.charCodeAt(0) */;
173 | };
174 |
175 | /*
176 | @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
177 | */
178 | const LUAL_BUFFERSIZE = conf.LUAL_BUFFERSIZE || 8192;
179 |
180 | // See: http://croquetweak.blogspot.fr/2014/08/deconstructing-floats-frexp-and-ldexp.html
181 | const frexp = function(value) {
182 | if (value === 0) return [value, 0];
183 | var data = new DataView(new ArrayBuffer(8));
184 | data.setFloat64(0, value);
185 | var bits = (data.getUint32(0) >>> 20) & 0x7FF;
186 | if (bits === 0) { // denormal
187 | data.setFloat64(0, value * Math.pow(2, 64)); // exp + 64
188 | bits = ((data.getUint32(0) >>> 20) & 0x7FF) - 64;
189 | }
190 | var exponent = bits - 1022;
191 | var mantissa = ldexp(value, -exponent);
192 | return [mantissa, exponent];
193 | };
194 |
195 | const ldexp = function(mantissa, exponent) {
196 | var steps = Math.min(3, Math.ceil(Math.abs(exponent) / 1023));
197 | var result = mantissa;
198 | for (var i = 0; i < steps; i++)
199 | result *= Math.pow(2, Math.floor((exponent + i) / steps));
200 | return result;
201 | };
202 |
203 | module.exports.LUAI_MAXSTACK = LUAI_MAXSTACK;
204 | module.exports.LUA_COMPAT_FLOATSTRING = LUA_COMPAT_FLOATSTRING;
205 | module.exports.LUA_IDSIZE = LUA_IDSIZE;
206 | module.exports.LUA_INTEGER_FMT = LUA_INTEGER_FMT;
207 | module.exports.LUA_INTEGER_FRMLEN = LUA_INTEGER_FRMLEN;
208 | module.exports.LUA_MAXINTEGER = LUA_MAXINTEGER;
209 | module.exports.LUA_MININTEGER = LUA_MININTEGER;
210 | module.exports.LUA_NUMBER_FMT = LUA_NUMBER_FMT;
211 | module.exports.LUA_NUMBER_FRMLEN = LUA_NUMBER_FRMLEN;
212 | module.exports.LUAL_BUFFERSIZE = LUAL_BUFFERSIZE;
213 | module.exports.frexp = frexp;
214 | module.exports.ldexp = ldexp;
215 | module.exports.lua_getlocaledecpoint = lua_getlocaledecpoint;
216 | module.exports.lua_integer2str = lua_integer2str;
217 | module.exports.lua_number2str = lua_number2str;
218 | module.exports.lua_numbertointeger = lua_numbertointeger;
219 |
--------------------------------------------------------------------------------
/src/lualib.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | LUA_VERSION_MAJOR,
5 | LUA_VERSION_MINOR
6 | } = require("./lua.js");
7 |
8 | const LUA_VERSUFFIX = "_" + LUA_VERSION_MAJOR + "_" + LUA_VERSION_MINOR;
9 | module.exports.LUA_VERSUFFIX = LUA_VERSUFFIX;
10 |
11 | module.exports.lua_assert = function(c) {};
12 |
13 | module.exports.luaopen_base = require("./lbaselib.js").luaopen_base;
14 |
15 | const LUA_COLIBNAME = "coroutine";
16 | module.exports.LUA_COLIBNAME = LUA_COLIBNAME;
17 | module.exports.luaopen_coroutine = require("./lcorolib.js").luaopen_coroutine;
18 |
19 | const LUA_TABLIBNAME = "table";
20 | module.exports.LUA_TABLIBNAME = LUA_TABLIBNAME;
21 | module.exports.luaopen_table = require("./ltablib.js").luaopen_table;
22 |
23 | if (typeof process !== "undefined") {
24 | const LUA_IOLIBNAME = "io";
25 | module.exports.LUA_IOLIBNAME = LUA_IOLIBNAME;
26 | module.exports.luaopen_io = require("./liolib.js").luaopen_io;
27 | }
28 |
29 | const LUA_OSLIBNAME = "os";
30 | module.exports.LUA_OSLIBNAME = LUA_OSLIBNAME;
31 | module.exports.luaopen_os = require("./loslib.js").luaopen_os;
32 |
33 | const LUA_STRLIBNAME = "string";
34 | module.exports.LUA_STRLIBNAME = LUA_STRLIBNAME;
35 | module.exports.luaopen_string = require("./lstrlib.js").luaopen_string;
36 |
37 | const LUA_UTF8LIBNAME = "utf8";
38 | module.exports.LUA_UTF8LIBNAME = LUA_UTF8LIBNAME;
39 | module.exports.luaopen_utf8 = require("./lutf8lib.js").luaopen_utf8;
40 |
41 | const LUA_BITLIBNAME = "bit32";
42 | module.exports.LUA_BITLIBNAME = LUA_BITLIBNAME;
43 | // module.exports.luaopen_bit32 = require("./lbitlib.js").luaopen_bit32;
44 |
45 | const LUA_MATHLIBNAME = "math";
46 | module.exports.LUA_MATHLIBNAME = LUA_MATHLIBNAME;
47 | module.exports.luaopen_math = require("./lmathlib.js").luaopen_math;
48 |
49 | const LUA_DBLIBNAME = "debug";
50 | module.exports.LUA_DBLIBNAME = LUA_DBLIBNAME;
51 | module.exports.luaopen_debug = require("./ldblib.js").luaopen_debug;
52 |
53 | const LUA_LOADLIBNAME = "package";
54 | module.exports.LUA_LOADLIBNAME = LUA_LOADLIBNAME;
55 | module.exports.luaopen_package = require("./loadlib.js").luaopen_package;
56 |
57 | const LUA_FENGARILIBNAME = "fengari";
58 | module.exports.LUA_FENGARILIBNAME = LUA_FENGARILIBNAME;
59 | module.exports.luaopen_fengari = require("./fengarilib.js").luaopen_fengari;
60 |
61 | const linit = require('./linit.js');
62 | module.exports.luaL_openlibs = linit.luaL_openlibs;
63 |
--------------------------------------------------------------------------------
/src/lundump.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | LUA_SIGNATURE,
5 | constant_types: {
6 | LUA_TBOOLEAN,
7 | LUA_TLNGSTR,
8 | LUA_TNIL,
9 | LUA_TNUMFLT,
10 | LUA_TNUMINT,
11 | LUA_TSHRSTR
12 | },
13 | thread_status: { LUA_ERRSYNTAX },
14 | is_luastring,
15 | luastring_eq,
16 | to_luastring
17 | } = require('./defs.js');
18 | const ldo = require('./ldo.js');
19 | const lfunc = require('./lfunc.js');
20 | const lobject = require('./lobject.js');
21 | const {
22 | MAXARG_sBx,
23 | POS_A,
24 | POS_Ax,
25 | POS_B,
26 | POS_Bx,
27 | POS_C,
28 | POS_OP,
29 | SIZE_A,
30 | SIZE_Ax,
31 | SIZE_B,
32 | SIZE_Bx,
33 | SIZE_C,
34 | SIZE_OP
35 | } = require('./lopcodes.js');
36 | const { lua_assert } = require("./llimits.js");
37 | const { luaS_bless } = require('./lstring.js');
38 | const {
39 | luaZ_read,
40 | ZIO
41 | } = require('./lzio.js');
42 |
43 | let LUAC_DATA = [0x19, 0x93, 13, 10, 0x1a, 10];
44 |
45 | class BytecodeParser {
46 |
47 | constructor(L, Z, name) {
48 | this.intSize = 4;
49 | this.size_tSize = 4;
50 | this.instructionSize = 4;
51 | this.integerSize = 4;
52 | this.numberSize = 8;
53 |
54 | lua_assert(Z instanceof ZIO, "BytecodeParser only operates on a ZIO");
55 | lua_assert(is_luastring(name));
56 |
57 | if (name[0] === 64 /* ('@').charCodeAt(0) */ || name[0] === 61 /* ('=').charCodeAt(0) */)
58 | this.name = name.subarray(1);
59 | else if (name[0] == LUA_SIGNATURE[0])
60 | this.name = to_luastring("binary string", true);
61 | else
62 | this.name = name;
63 |
64 | this.L = L;
65 | this.Z = Z;
66 |
67 | // Used to do buffer to number conversions
68 | this.arraybuffer = new ArrayBuffer(
69 | Math.max(this.intSize, this.size_tSize, this.instructionSize, this.integerSize, this.numberSize)
70 | );
71 | this.dv = new DataView(this.arraybuffer);
72 | this.u8 = new Uint8Array(this.arraybuffer);
73 | }
74 |
75 | read(size) {
76 | let u8 = new Uint8Array(size);
77 | if(luaZ_read(this.Z, u8, 0, size) !== 0)
78 | this.error("truncated");
79 | return u8;
80 | }
81 |
82 | LoadByte() {
83 | if (luaZ_read(this.Z, this.u8, 0, 1) !== 0)
84 | this.error("truncated");
85 | return this.u8[0];
86 | }
87 |
88 | LoadInt() {
89 | if (luaZ_read(this.Z, this.u8, 0, this.intSize) !== 0)
90 | this.error("truncated");
91 | return this.dv.getInt32(0, true);
92 | }
93 |
94 | LoadNumber() {
95 | if (luaZ_read(this.Z, this.u8, 0, this.numberSize) !== 0)
96 | this.error("truncated");
97 | return this.dv.getFloat64(0, true);
98 | }
99 |
100 | LoadInteger() {
101 | if (luaZ_read(this.Z, this.u8, 0, this.integerSize) !== 0)
102 | this.error("truncated");
103 | return this.dv.getInt32(0, true);
104 | }
105 |
106 | LoadSize_t() {
107 | return this.LoadInteger();
108 | }
109 |
110 | LoadString() {
111 | let size = this.LoadByte();
112 | if (size === 0xFF)
113 | size = this.LoadSize_t();
114 | if (size === 0)
115 | return null;
116 | return luaS_bless(this.L, this.read(size-1));
117 | }
118 |
119 | /* creates a mask with 'n' 1 bits at position 'p' */
120 | static MASK1(n, p) {
121 | return ((~((~0)<<(n)))<<(p));
122 | }
123 |
124 | LoadCode(f) {
125 | let n = this.LoadInt();
126 | let p = BytecodeParser;
127 |
128 | for (let i = 0; i < n; i++) {
129 | if (luaZ_read(this.Z, this.u8, 0, this.instructionSize) !== 0)
130 | this.error("truncated");
131 | let ins = this.dv.getUint32(0, true);
132 | f.code[i] = {
133 | code: ins,
134 | opcode: (ins >> POS_OP) & p.MASK1(SIZE_OP, 0),
135 | A: (ins >> POS_A) & p.MASK1(SIZE_A, 0),
136 | B: (ins >> POS_B) & p.MASK1(SIZE_B, 0),
137 | C: (ins >> POS_C) & p.MASK1(SIZE_C, 0),
138 | Bx: (ins >> POS_Bx) & p.MASK1(SIZE_Bx, 0),
139 | Ax: (ins >> POS_Ax) & p.MASK1(SIZE_Ax, 0),
140 | sBx: ((ins >> POS_Bx) & p.MASK1(SIZE_Bx, 0)) - MAXARG_sBx
141 | };
142 | }
143 | }
144 |
145 | LoadConstants(f) {
146 | let n = this.LoadInt();
147 |
148 | for (let i = 0; i < n; i++) {
149 | let t = this.LoadByte();
150 |
151 | switch (t) {
152 | case LUA_TNIL:
153 | f.k.push(new lobject.TValue(LUA_TNIL, null));
154 | break;
155 | case LUA_TBOOLEAN:
156 | f.k.push(new lobject.TValue(LUA_TBOOLEAN, this.LoadByte() !== 0));
157 | break;
158 | case LUA_TNUMFLT:
159 | f.k.push(new lobject.TValue(LUA_TNUMFLT, this.LoadNumber()));
160 | break;
161 | case LUA_TNUMINT:
162 | f.k.push(new lobject.TValue(LUA_TNUMINT, this.LoadInteger()));
163 | break;
164 | case LUA_TSHRSTR:
165 | case LUA_TLNGSTR:
166 | f.k.push(new lobject.TValue(LUA_TLNGSTR, this.LoadString()));
167 | break;
168 | default:
169 | this.error(`unrecognized constant '${t}'`);
170 | }
171 | }
172 | }
173 |
174 | LoadProtos(f) {
175 | let n = this.LoadInt();
176 |
177 | for (let i = 0; i < n; i++) {
178 | f.p[i] = new lfunc.Proto(this.L);
179 | this.LoadFunction(f.p[i], f.source);
180 | }
181 | }
182 |
183 | LoadUpvalues(f) {
184 | let n = this.LoadInt();
185 |
186 | for (let i = 0; i < n; i++) {
187 | f.upvalues[i] = {
188 | name: null,
189 | instack: this.LoadByte(),
190 | idx: this.LoadByte()
191 | };
192 | }
193 | }
194 |
195 | LoadDebug(f) {
196 | let n = this.LoadInt();
197 | for (let i = 0; i < n; i++)
198 | f.lineinfo[i] = this.LoadInt();
199 |
200 | n = this.LoadInt();
201 | for (let i = 0; i < n; i++) {
202 | f.locvars[i] = {
203 | varname: this.LoadString(),
204 | startpc: this.LoadInt(),
205 | endpc: this.LoadInt()
206 | };
207 | }
208 |
209 | n = this.LoadInt();
210 | for (let i = 0; i < n; i++) {
211 | f.upvalues[i].name = this.LoadString();
212 | }
213 | }
214 |
215 | LoadFunction(f, psource) {
216 | f.source = this.LoadString();
217 | if (f.source === null) /* no source in dump? */
218 | f.source = psource; /* reuse parent's source */
219 | f.linedefined = this.LoadInt();
220 | f.lastlinedefined = this.LoadInt();
221 | f.numparams = this.LoadByte();
222 | f.is_vararg = this.LoadByte() !== 0;
223 | f.maxstacksize = this.LoadByte();
224 | this.LoadCode(f);
225 | this.LoadConstants(f);
226 | this.LoadUpvalues(f);
227 | this.LoadProtos(f);
228 | this.LoadDebug(f);
229 | }
230 |
231 | checkliteral(s, msg) {
232 | let buff = this.read(s.length);
233 | if (!luastring_eq(buff, s))
234 | this.error(msg);
235 | }
236 |
237 | checkHeader() {
238 | this.checkliteral(LUA_SIGNATURE.subarray(1), "not a"); /* 1st char already checked */
239 |
240 | if (this.LoadByte() !== 0x53)
241 | this.error("version mismatch in");
242 |
243 | if (this.LoadByte() !== 0)
244 | this.error("format mismatch in");
245 |
246 | this.checkliteral(LUAC_DATA, "corrupted");
247 |
248 | this.intSize = this.LoadByte();
249 | this.size_tSize = this.LoadByte();
250 | this.instructionSize = this.LoadByte();
251 | this.integerSize = this.LoadByte();
252 | this.numberSize = this.LoadByte();
253 |
254 | this.checksize(this.intSize, 4, "int");
255 | this.checksize(this.size_tSize, 4, "size_t");
256 | this.checksize(this.instructionSize, 4, "instruction");
257 | this.checksize(this.integerSize, 4, "integer");
258 | this.checksize(this.numberSize, 8, "number");
259 |
260 | if (this.LoadInteger() !== 0x5678)
261 | this.error("endianness mismatch in");
262 |
263 | if (this.LoadNumber() !== 370.5)
264 | this.error("float format mismatch in");
265 |
266 | }
267 |
268 | error(why) {
269 | lobject.luaO_pushfstring(this.L, to_luastring("%s: %s precompiled chunk"), this.name, to_luastring(why));
270 | ldo.luaD_throw(this.L, LUA_ERRSYNTAX);
271 | }
272 |
273 | checksize(byte, size, tname) {
274 | if (byte !== size)
275 | this.error(`${tname} size mismatch in`);
276 | }
277 | }
278 |
279 | const luaU_undump = function(L, Z, name) {
280 | let S = new BytecodeParser(L, Z, name);
281 | S.checkHeader();
282 | let cl = lfunc.luaF_newLclosure(L, S.LoadByte());
283 | ldo.luaD_inctop(L);
284 | L.stack[L.top-1].setclLvalue(cl);
285 | cl.p = new lfunc.Proto(L);
286 | S.LoadFunction(cl.p, null);
287 | lua_assert(cl.nupvalues === cl.p.upvalues.length);
288 | /* luai_verifycode */
289 | return cl;
290 | };
291 |
292 | module.exports.luaU_undump = luaU_undump;
293 |
--------------------------------------------------------------------------------
/src/lutf8lib.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | lua_gettop,
5 | lua_pushcfunction,
6 | lua_pushfstring,
7 | lua_pushinteger,
8 | lua_pushnil,
9 | lua_pushstring,
10 | lua_pushvalue,
11 | lua_setfield,
12 | lua_tointeger
13 | } = require('./lua.js');
14 | const {
15 | luaL_Buffer,
16 | luaL_addvalue,
17 | luaL_argcheck,
18 | luaL_buffinit,
19 | luaL_checkinteger,
20 | luaL_checkstack,
21 | luaL_checkstring,
22 | luaL_error,
23 | luaL_newlib,
24 | luaL_optinteger,
25 | luaL_pushresult
26 | } = require('./lauxlib.js');
27 | const {
28 | luastring_of,
29 | to_luastring
30 | } = require("./fengaricore.js");
31 |
32 | const MAXUNICODE = 0x10FFFF;
33 |
34 | const iscont = function(p) {
35 | let c = p & 0xC0;
36 | return c === 0x80;
37 | };
38 |
39 | /* translate a relative string position: negative means back from end */
40 | const u_posrelat = function(pos, len) {
41 | if (pos >= 0) return pos;
42 | else if (0 - pos > len) return 0;
43 | else return len + pos + 1;
44 | };
45 |
46 | /*
47 | ** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
48 | */
49 | const limits = [0xFF, 0x7F, 0x7FF, 0xFFFF];
50 | const utf8_decode = function(s, pos) {
51 | let c = s[pos];
52 | let res = 0; /* final result */
53 | if (c < 0x80) /* ascii? */
54 | res = c;
55 | else {
56 | let count = 0; /* to count number of continuation bytes */
57 | while (c & 0x40) { /* still have continuation bytes? */
58 | let cc = s[pos + (++count)]; /* read next byte */
59 | if ((cc & 0xC0) !== 0x80) /* not a continuation byte? */
60 | return null; /* invalid byte sequence */
61 | res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
62 | c <<= 1; /* to test next bit */
63 | }
64 | res |= ((c & 0x7F) << (count * 5)); /* add first byte */
65 | if (count > 3 || res > MAXUNICODE || res <= limits[count])
66 | return null; /* invalid byte sequence */
67 | pos += count; /* skip continuation bytes read */
68 | }
69 |
70 | return {
71 | code: res,
72 | pos: pos + 1
73 | };
74 | };
75 |
76 | /*
77 | ** utf8len(s [, i [, j]]) --> number of characters that start in the
78 | ** range [i,j], or nil + current position if 's' is not well formed in
79 | ** that interval
80 | */
81 | const utflen = function(L) {
82 | let n = 0;
83 | let s = luaL_checkstring(L, 1);
84 | let len = s.length;
85 | let posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
86 | let posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
87 |
88 | luaL_argcheck(L, 1 <= posi && --posi <= len, 2, "initial position out of string");
89 | luaL_argcheck(L, --posj < len, 3, "final position out of string");
90 |
91 | while (posi <= posj) {
92 | let dec = utf8_decode(s, posi);
93 | if (dec === null) { /* conversion error? */
94 | lua_pushnil(L); /* return nil ... */
95 | lua_pushinteger(L, posi + 1); /* ... and current position */
96 | return 2;
97 | }
98 | posi = dec.pos;
99 | n++;
100 | }
101 | lua_pushinteger(L, n);
102 | return 1;
103 | };
104 |
105 | const p_U = to_luastring("%U");
106 | const pushutfchar = function(L, arg) {
107 | let code = luaL_checkinteger(L, arg);
108 | luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range");
109 | lua_pushfstring(L, p_U, code);
110 | };
111 |
112 | /*
113 | ** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
114 | */
115 | const utfchar = function(L) {
116 | let n = lua_gettop(L); /* number of arguments */
117 | if (n === 1) /* optimize common case of single char */
118 | pushutfchar(L, 1);
119 | else {
120 | let b = new luaL_Buffer();
121 | luaL_buffinit(L, b);
122 | for (let i = 1; i <= n; i++) {
123 | pushutfchar(L, i);
124 | luaL_addvalue(b);
125 | }
126 | luaL_pushresult(b);
127 | }
128 | return 1;
129 | };
130 |
131 | /*
132 | ** offset(s, n, [i]) -> index where n-th character counting from
133 | ** position 'i' starts; 0 means character at 'i'.
134 | */
135 | const byteoffset = function(L) {
136 | let s = luaL_checkstring(L, 1);
137 | let n = luaL_checkinteger(L, 2);
138 | let posi = n >= 0 ? 1 : s.length + 1;
139 | posi = u_posrelat(luaL_optinteger(L, 3, posi), s.length);
140 |
141 | luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, "position out of range");
142 |
143 | if (n === 0) {
144 | /* find beginning of current byte sequence */
145 | while (posi > 0 && iscont(s[posi])) posi--;
146 | } else {
147 | if (iscont(s[posi]))
148 | luaL_error(L, "initial position is a continuation byte");
149 |
150 | if (n < 0) {
151 | while (n < 0 && posi > 0) { /* move back */
152 | do { /* find beginning of previous character */
153 | posi--;
154 | } while (posi > 0 && iscont(s[posi]));
155 | n++;
156 | }
157 | } else {
158 | n--; /* do not move for 1st character */
159 | while (n > 0 && posi < s.length) {
160 | do { /* find beginning of next character */
161 | posi++;
162 | } while (iscont(s[posi])); /* (cannot pass final '\0') */
163 | n--;
164 | }
165 | }
166 | }
167 |
168 | if (n === 0) /* did it find given character? */
169 | lua_pushinteger(L, posi + 1);
170 | else /* no such character */
171 | lua_pushnil(L);
172 |
173 | return 1;
174 | };
175 |
176 | /*
177 | ** codepoint(s, [i, [j]]) -> returns codepoints for all characters
178 | ** that start in the range [i,j]
179 | */
180 | const codepoint = function(L) {
181 | let s = luaL_checkstring(L, 1);
182 | let posi = u_posrelat(luaL_optinteger(L, 2, 1), s.length);
183 | let pose = u_posrelat(luaL_optinteger(L, 3, posi), s.length);
184 |
185 | luaL_argcheck(L, posi >= 1, 2, "out of range");
186 | luaL_argcheck(L, pose <= s.length, 3, "out of range");
187 |
188 | if (posi > pose) return 0; /* empty interval; return no values */
189 | if (pose - posi >= Number.MAX_SAFE_INTEGER)
190 | return luaL_error(L, "string slice too long");
191 | let n = (pose - posi) + 1;
192 | luaL_checkstack(L, n, "string slice too long");
193 | n = 0;
194 | for (posi -= 1; posi < pose;) {
195 | let dec = utf8_decode(s, posi);
196 | if (dec === null)
197 | return luaL_error(L, "invalid UTF-8 code");
198 | lua_pushinteger(L, dec.code);
199 | posi = dec.pos;
200 | n++;
201 | }
202 | return n;
203 | };
204 |
205 | const iter_aux = function(L) {
206 | let s = luaL_checkstring(L, 1);
207 | let len = s.length;
208 | let n = lua_tointeger(L, 2) - 1;
209 |
210 | if (n < 0) /* first iteration? */
211 | n = 0; /* start from here */
212 | else if (n < len) {
213 | n++; /* skip current byte */
214 | while (iscont(s[n])) n++; /* and its continuations */
215 | }
216 |
217 | if (n >= len)
218 | return 0; /* no more codepoints */
219 | else {
220 | let dec = utf8_decode(s, n);
221 | if (dec === null || iscont(s[dec.pos]))
222 | return luaL_error(L, to_luastring("invalid UTF-8 code"));
223 | lua_pushinteger(L, n + 1);
224 | lua_pushinteger(L, dec.code);
225 | return 2;
226 | }
227 | };
228 |
229 | const iter_codes = function(L) {
230 | luaL_checkstring(L, 1);
231 | lua_pushcfunction(L, iter_aux);
232 | lua_pushvalue(L, 1);
233 | lua_pushinteger(L, 0);
234 | return 3;
235 | };
236 |
237 | const funcs = {
238 | "char": utfchar,
239 | "codepoint": codepoint,
240 | "codes": iter_codes,
241 | "len": utflen,
242 | "offset": byteoffset
243 | };
244 |
245 | /* pattern to match a single UTF-8 character */
246 | const UTF8PATT = luastring_of(91, 0, 45, 127, 194, 45, 244, 93, 91, 128, 45, 191, 93, 42);
247 |
248 | const luaopen_utf8 = function(L) {
249 | luaL_newlib(L, funcs);
250 | lua_pushstring(L, UTF8PATT);
251 | lua_setfield(L, -2, to_luastring("charpattern", true));
252 | return 1;
253 | };
254 |
255 | module.exports.luaopen_utf8 = luaopen_utf8;
256 |
--------------------------------------------------------------------------------
/src/lzio.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { lua_assert } = require("./llimits.js");
4 |
5 | class MBuffer {
6 | constructor() {
7 | this.buffer = null;
8 | this.n = 0;
9 | }
10 | }
11 |
12 | const luaZ_buffer = function(buff) {
13 | return buff.buffer.subarray(0, buff.n);
14 | };
15 |
16 | const luaZ_buffremove = function(buff, i) {
17 | buff.n -= i;
18 | };
19 |
20 | const luaZ_resetbuffer = function(buff) {
21 | buff.n = 0;
22 | };
23 |
24 | const luaZ_resizebuffer = function(L, buff, size) {
25 | let newbuff = new Uint8Array(size);
26 | if (buff.buffer)
27 | newbuff.set(buff.buffer);
28 | buff.buffer = newbuff;
29 | };
30 |
31 | class ZIO {
32 | constructor(L, reader, data) {
33 | this.L = L; /* Lua state (for reader) */
34 | lua_assert(typeof reader == "function", "ZIO requires a reader");
35 | this.reader = reader; /* reader function */
36 | this.data = data; /* additional data */
37 | this.n = 0; /* bytes still unread */
38 | this.buffer = null;
39 | this.off = 0; /* current position in buffer */
40 | }
41 |
42 | zgetc () {
43 | return ((this.n--) > 0) ? this.buffer[this.off++] : luaZ_fill(this);
44 | }
45 | }
46 |
47 | const EOZ = -1;
48 |
49 | const luaZ_fill = function(z) {
50 | let buff = z.reader(z.L, z.data);
51 | if (buff === null)
52 | return EOZ;
53 | lua_assert(buff instanceof Uint8Array, "Should only load binary of array of bytes");
54 | let size = buff.length;
55 | if (size === 0)
56 | return EOZ;
57 | z.buffer = buff;
58 | z.off = 0;
59 | z.n = size - 1;
60 | return z.buffer[z.off++];
61 | };
62 |
63 | /* b should be an array-like that will be set to bytes
64 | * b_offset is the offset at which to start filling */
65 | const luaZ_read = function(z, b, b_offset, n) {
66 | while (n) {
67 | if (z.n === 0) { /* no bytes in buffer? */
68 | if (luaZ_fill(z) === EOZ)
69 | return n; /* no more input; return number of missing bytes */
70 | else {
71 | z.n++; /* luaZ_fill consumed first byte; put it back */
72 | z.off--;
73 | }
74 | }
75 | let m = (n <= z.n) ? n : z.n; /* min. between n and z->n */
76 | for (let i=0; i {
43 | unicode_tests.forEach((v) => {
44 | test(v.description, () => {
45 | expect(defs.to_luastring(v.literal)).toEqual(v.byte_array);
46 | });
47 | });
48 | });
49 |
50 | describe('to_jsstring', () => {
51 | unicode_tests.forEach((v) => {
52 | test(v.description, () => {
53 | expect(defs.to_jsstring(v.byte_array)).toEqual(v.literal);
54 | });
55 | });
56 | });
57 |
58 |
59 | describe('to_jsstring fails on invalid unicode', () => {
60 | test("non-utf8 char", () => {
61 | expect(() => defs.to_jsstring(defs.luastring_of(165)))
62 | .toThrowError(RangeError);
63 | });
64 |
65 | test("invalid continuation byte", () => {
66 | expect(() => defs.to_jsstring(defs.luastring_of(208, 60)))
67 | .toThrowError(RangeError);
68 | });
69 |
70 | test("invalid continuation byte", () => {
71 | expect(() => defs.to_jsstring(defs.luastring_of(225, 60, 145)))
72 | .toThrowError(RangeError);
73 | });
74 |
75 | test("invalid continuation byte", () => {
76 | expect(() => defs.to_jsstring(defs.luastring_of(225, 145, 60)))
77 | .toThrowError(RangeError);
78 | });
79 |
80 | test("invalid continuation byte", () => {
81 | expect(() => defs.to_jsstring(defs.luastring_of(242, 60, 145, 145)))
82 | .toThrowError(RangeError);
83 | });
84 |
85 | test("invalid continuation byte", () => {
86 | expect(() => defs.to_jsstring(defs.luastring_of(242, 145, 60, 145)))
87 | .toThrowError(RangeError);
88 | });
89 |
90 | test("invalid continuation byte", () => {
91 | expect(() => defs.to_jsstring(defs.luastring_of(242, 145, 145, 60)))
92 | .toThrowError(RangeError);
93 | });
94 | });
95 |
--------------------------------------------------------------------------------
/test/lauxlib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require("../src/lauxlib.js");
5 | const {to_luastring} = require("../src/fengaricore.js");
6 |
7 |
8 | test('luaL_ref, lua_rawgeti, luaL_unref, LUA_REGISTRYINDEX', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | {
13 | lua.lua_pushstring(L, to_luastring("hello references!"));
14 |
15 | let r = lauxlib.luaL_ref(L, lua.LUA_REGISTRYINDEX); // pops a value, stores it and returns a reference
16 | lua.lua_rawgeti(L, lua.LUA_REGISTRYINDEX, r); // pushes a value associated with the reference
17 | lauxlib.luaL_unref(L, lua.LUA_REGISTRYINDEX, r); // releases the reference
18 | }
19 |
20 | expect(lua.lua_tojsstring(L, -1))
21 | .toBe("hello references!");
22 | });
23 |
--------------------------------------------------------------------------------
/test/lcorolib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const lstate = require('../src/lstate.js');
7 | const {to_luastring} = require("../src/fengaricore.js");
8 |
9 | test('coroutine.create, coroutine.yield, coroutine.resume', () => {
10 | let L = lauxlib.luaL_newstate();
11 | if (!L) throw Error("failed to create lua state");
12 |
13 | let luaCode = `
14 | local co = coroutine.create(function (start)
15 | local b = coroutine.yield(start * start);
16 | coroutine.yield(b * b)
17 | end)
18 |
19 | local success, pow = coroutine.resume(co, 5)
20 | success, pow = coroutine.resume(co, pow)
21 |
22 | return pow
23 | `;
24 | {
25 | lualib.luaL_openlibs(L);
26 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
27 | lua.lua_call(L, 0, -1);
28 | }
29 |
30 | expect(lua.lua_tonumber(L, -1))
31 | .toBe(625);
32 | });
33 |
34 |
35 | test('coroutine.status', () => {
36 | let L = lauxlib.luaL_newstate();
37 | if (!L) throw Error("failed to create lua state");
38 |
39 | let luaCode = `
40 | local co = coroutine.create(function (start)
41 | local b = coroutine.yield(start * start);
42 | coroutine.yield(b * b)
43 | end)
44 |
45 | local s1 = coroutine.status(co)
46 |
47 | local success, pow = coroutine.resume(co, 5)
48 | success, pow = coroutine.resume(co, pow)
49 |
50 | coroutine.resume(co, pow)
51 |
52 | local s2 = coroutine.status(co)
53 |
54 | return s1, s2
55 | `;
56 | {
57 | lualib.luaL_openlibs(L);
58 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
59 | lua.lua_call(L, 0, -1);
60 | }
61 |
62 | expect(lua.lua_tojsstring(L, -2))
63 | .toBe("suspended");
64 |
65 | expect(lua.lua_tojsstring(L, -1))
66 | .toBe("dead");
67 | });
68 |
69 |
70 | test('coroutine.isyieldable', () => {
71 | let L = lauxlib.luaL_newstate();
72 | if (!L) throw Error("failed to create lua state");
73 |
74 | let luaCode = `
75 | local co = coroutine.create(function ()
76 | coroutine.yield(coroutine.isyieldable());
77 | end)
78 |
79 | local succes, yieldable = coroutine.resume(co)
80 |
81 | return yieldable, coroutine.isyieldable()
82 | `;
83 | {
84 | lualib.luaL_openlibs(L);
85 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
86 | lua.lua_call(L, 0, -1);
87 | }
88 |
89 | expect(lua.lua_toboolean(L, -2)).toBe(true);
90 | expect(lua.lua_toboolean(L, -1)).toBe(false);
91 | });
92 |
93 |
94 | test('coroutine.running', () => {
95 | let L = lauxlib.luaL_newstate();
96 | if (!L) throw Error("failed to create lua state");
97 |
98 | let luaCode = `
99 | local running, ismain
100 |
101 | local co = coroutine.create(function ()
102 | running, ismain = coroutine.running()
103 | end)
104 |
105 | coroutine.resume(co)
106 |
107 | return running, ismain
108 | `;
109 | {
110 | lualib.luaL_openlibs(L);
111 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
112 | lua.lua_call(L, 0, -1);
113 | }
114 |
115 | expect(lua.lua_tothread(L, -2)).toBeInstanceOf(lstate.lua_State);
116 | expect(lua.lua_toboolean(L, -1)).toBe(false);
117 | });
118 |
119 |
120 | test('coroutine.wrap', () => {
121 | let L = lauxlib.luaL_newstate();
122 | if (!L) throw Error("failed to create lua state");
123 |
124 | let luaCode = `
125 | local co = coroutine.wrap(function (start)
126 | local b = coroutine.yield(start * start);
127 | coroutine.yield(b * b)
128 | end)
129 |
130 | pow = co(5)
131 | pow = co(pow)
132 |
133 | return pow
134 | `;
135 | {
136 | lualib.luaL_openlibs(L);
137 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
138 | lua.lua_call(L, 0, -1);
139 | }
140 |
141 | expect(lua.lua_tonumber(L, -1))
142 | .toBe(625);
143 | });
144 |
--------------------------------------------------------------------------------
/test/ldblib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | test('debug.sethook', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | local result = ""
14 |
15 | debug.sethook(function (event)
16 | result = result .. event .. " "
17 | end, "crl", 1)
18 |
19 | local l = function() end
20 |
21 | l()
22 | l()
23 | l()
24 |
25 | return result
26 | `;
27 | {
28 | lualib.luaL_openlibs(L);
29 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
30 | lua.lua_call(L, 0, -1);
31 | }
32 |
33 | expect(lua.lua_tojsstring(L, -1))
34 | .toBe("return count line count line count line call count line return count line count line call count line return count line count line call count line return count line ");
35 | });
36 |
37 |
38 | test('debug.gethook', () => {
39 | let L = lauxlib.luaL_newstate();
40 | if (!L) throw Error("failed to create lua state");
41 |
42 | let luaCode = `
43 | local result = ""
44 |
45 | debug.sethook(function (event)
46 | result = result .. event .. " "
47 | end, "crl", 1)
48 |
49 | local l = function() end
50 |
51 | l()
52 | l()
53 | l()
54 |
55 | return debug.gethook()
56 | `;
57 | {
58 | lualib.luaL_openlibs(L);
59 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
60 | lua.lua_call(L, 0, -1);
61 | }
62 |
63 | expect(lua.lua_typename(L, lua.lua_type(L, -3)))
64 | .toEqual(to_luastring("function"));
65 | expect(lua.lua_tojsstring(L, -2)).toBe("crl");
66 | expect(lua.lua_tointeger(L, -1)).toBe(1);
67 | });
68 |
69 |
70 | test('debug.getlocal', () => {
71 | let L = lauxlib.luaL_newstate();
72 | if (!L) throw Error("failed to create lua state");
73 |
74 | let luaCode = `
75 | local alocal = "alocal"
76 | local another = "another"
77 |
78 | local result = ""
79 |
80 | local l = function()
81 | local infunction = "infunction"
82 | local anotherin = "anotherin"
83 | result = table.concat(table.pack(debug.getlocal(2, 1)), " ")
84 | .. table.concat(table.pack(debug.getlocal(2, 2)), " ")
85 | .. table.concat(table.pack(debug.getlocal(1, 1)), " ")
86 | .. table.concat(table.pack(debug.getlocal(1, 2)), " ")
87 | end
88 |
89 | l()
90 |
91 | return result
92 | `;
93 | {
94 | lualib.luaL_openlibs(L);
95 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
96 | lua.lua_call(L, 0, -1);
97 | }
98 |
99 | expect(lua.lua_tojsstring(L, -1))
100 | .toBe("alocal alocalanother anotherinfunction infunctionanotherin anotherin");
101 | });
102 |
103 | test('debug.setlocal', () => {
104 | let L = lauxlib.luaL_newstate();
105 | if (!L) throw Error("failed to create lua state");
106 |
107 | let luaCode = `
108 | local alocal = "alocal"
109 | local another = "another"
110 |
111 | local l = function()
112 | local infunction = "infunction"
113 | local anotherin = "anotherin"
114 |
115 | debug.setlocal(2, 1, 1)
116 | debug.setlocal(2, 2, 2)
117 | debug.setlocal(1, 1, 3)
118 | debug.setlocal(1, 2, 4)
119 |
120 | return infunction, anotherin
121 | end
122 |
123 | local a, b = l()
124 |
125 | return alocal, another, a, b
126 | `;
127 | {
128 | lualib.luaL_openlibs(L);
129 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
130 | lua.lua_call(L, 0, -1);
131 | }
132 |
133 | expect(lua.lua_tointeger(L, -4)).toBe(1);
134 | expect(lua.lua_tointeger(L, -3)).toBe(2);
135 | expect(lua.lua_tointeger(L, -2)).toBe(3);
136 | expect(lua.lua_tointeger(L, -1)).toBe(4);
137 | });
138 |
139 | test('debug.upvalueid', () => {
140 | let L = lauxlib.luaL_newstate();
141 | if (!L) throw Error("failed to create lua state");
142 |
143 | let luaCode = `
144 | local upvalue = "upvalue"
145 |
146 | local l = function()
147 | return upvalue
148 | end
149 |
150 | return debug.upvalueid(l, 1)
151 | `;
152 | {
153 | lualib.luaL_openlibs(L);
154 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
155 | lua.lua_call(L, 0, -1);
156 | }
157 |
158 | expect(lua.lua_touserdata(L, -1)).toBeTruthy();
159 | });
160 |
161 |
162 | test('debug.upvaluejoin', () => {
163 | let L = lauxlib.luaL_newstate();
164 | if (!L) throw Error("failed to create lua state");
165 |
166 | let luaCode = `
167 | local upvalue1 = "upvalue1"
168 | local upvalue2 = "upvalue2"
169 |
170 | local l1 = function()
171 | return upvalue1
172 | end
173 |
174 | local l2 = function()
175 | return upvalue2
176 | end
177 |
178 | debug.upvaluejoin(l1, 1, l2, 1)
179 |
180 | return l1()
181 | `;
182 | {
183 | lualib.luaL_openlibs(L);
184 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
185 | lua.lua_call(L, 0, -1);
186 | }
187 |
188 | expect(lua.lua_tojsstring(L, -1))
189 | .toBe("upvalue2");
190 | });
191 |
192 |
193 | test('debug.traceback (with a global)', () => {
194 | let L = lauxlib.luaL_newstate();
195 | if (!L) throw Error("failed to create lua state");
196 |
197 | let luaCode = `
198 | local trace
199 |
200 | rec = function(n)
201 | n = n or 0
202 | if n < 10 then
203 | rec(n + 1)
204 | else
205 | trace = debug.traceback()
206 | end
207 | end
208 |
209 | rec()
210 |
211 | return trace
212 | `;
213 | {
214 | lualib.luaL_openlibs(L);
215 | luaCode = to_luastring(luaCode);
216 | lauxlib.luaL_loadbuffer(L, luaCode, luaCode.length, to_luastring("traceback-test"));
217 | lua.lua_call(L, 0, -1);
218 | }
219 |
220 | expect(lua.lua_tojsstring(L, -1))
221 | .toBe(`stack traceback:
222 | \t[string "traceback-test"]:9: in function 'rec'
223 | \t[string "traceback-test"]:7: in function 'rec'
224 | \t[string "traceback-test"]:7: in function 'rec'
225 | \t[string "traceback-test"]:7: in function 'rec'
226 | \t[string "traceback-test"]:7: in function 'rec'
227 | \t[string "traceback-test"]:7: in function 'rec'
228 | \t[string "traceback-test"]:7: in function 'rec'
229 | \t[string "traceback-test"]:7: in function 'rec'
230 | \t[string "traceback-test"]:7: in function 'rec'
231 | \t[string "traceback-test"]:7: in function 'rec'
232 | \t[string "traceback-test"]:7: in function 'rec'
233 | \t[string "traceback-test"]:13: in main chunk`);
234 | });
235 |
236 |
237 | test('debug.traceback (with a upvalue)', () => {
238 | let L = lauxlib.luaL_newstate();
239 | if (!L) throw Error("failed to create lua state");
240 |
241 | let luaCode = `
242 | local trace
243 | local rec
244 |
245 | rec = function(n)
246 | n = n or 0
247 | if n < 10 then
248 | rec(n + 1)
249 | else
250 | trace = debug.traceback()
251 | end
252 | end
253 |
254 | rec()
255 |
256 | return trace
257 | `;
258 | {
259 | lualib.luaL_openlibs(L);
260 | luaCode = to_luastring(luaCode);
261 | lauxlib.luaL_loadbuffer(L, luaCode, luaCode.length, to_luastring("traceback-test"));
262 | lua.lua_call(L, 0, -1);
263 | }
264 |
265 | expect(lua.lua_tojsstring(L, -1))
266 | .toBe(`stack traceback:
267 | \t[string "traceback-test"]:10: in upvalue 'rec'
268 | \t[string "traceback-test"]:8: in upvalue 'rec'
269 | \t[string "traceback-test"]:8: in upvalue 'rec'
270 | \t[string "traceback-test"]:8: in upvalue 'rec'
271 | \t[string "traceback-test"]:8: in upvalue 'rec'
272 | \t[string "traceback-test"]:8: in upvalue 'rec'
273 | \t[string "traceback-test"]:8: in upvalue 'rec'
274 | \t[string "traceback-test"]:8: in upvalue 'rec'
275 | \t[string "traceback-test"]:8: in upvalue 'rec'
276 | \t[string "traceback-test"]:8: in upvalue 'rec'
277 | \t[string "traceback-test"]:8: in local 'rec'
278 | \t[string "traceback-test"]:14: in main chunk`);
279 | });
280 |
281 | test('debug.getinfo', () => {
282 | let L = lauxlib.luaL_newstate();
283 | if (!L) throw Error("failed to create lua state");
284 |
285 | let luaCode = `
286 | local alocal = function(p1, p2) end
287 | global = function() return alocal end
288 |
289 | local d1 = debug.getinfo(alocal)
290 | local d2 = debug.getinfo(global)
291 |
292 | return d1.short_src, d1.nups, d1.what, d1.nparams,
293 | d2.short_src, d2.nups, d2.what, d2.nparams
294 | `;
295 | {
296 | lualib.luaL_openlibs(L);
297 | luaCode = to_luastring(luaCode);
298 | lauxlib.luaL_loadbuffer(L, luaCode, luaCode.length, to_luastring("getinfo-test"));
299 | lua.lua_call(L, 0, -1);
300 | }
301 |
302 | expect(lua.lua_tojsstring(L, -8)).toBe(`[string "getinfo-test"]`);
303 | expect(lua.lua_tointeger(L, -7)).toBe(0);
304 | expect(lua.lua_tojsstring(L, -6)).toBe(`Lua`);
305 | expect(lua.lua_tointeger(L, -5)).toBe(2);
306 | expect(lua.lua_tojsstring(L, -4)).toBe(`[string "getinfo-test"]`);
307 | expect(lua.lua_tointeger(L, -3)).toBe(1);
308 | expect(lua.lua_tojsstring(L, -2)).toBe(`Lua`);
309 | expect(lua.lua_tointeger(L, -1)).toBe(0);
310 | });
311 |
--------------------------------------------------------------------------------
/test/ldebug.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | test('luaG_typeerror', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | local a = true
14 | return #a
15 | `;
16 | {
17 | lualib.luaL_openlibs(L);
18 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
19 | lua.lua_pcall(L, 0, -1, 0);
20 | }
21 |
22 | expect(lua.lua_tojsstring(L, -1))
23 | .toMatch("attempt to get length of a boolean value (local 'a')");
24 | });
25 |
26 |
27 | test('luaG_typeerror', () => {
28 | let L = lauxlib.luaL_newstate();
29 | if (!L) throw Error("failed to create lua state");
30 |
31 | let luaCode = `
32 | local a = true
33 | return a.yo
34 | `;
35 | {
36 | lualib.luaL_openlibs(L);
37 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
38 | lua.lua_pcall(L, 0, -1, 0);
39 | }
40 |
41 | expect(lua.lua_tojsstring(L, -1))
42 | .toMatch("attempt to index a boolean value (local 'a')");
43 | });
44 |
45 |
46 | test('luaG_typeerror', () => {
47 | let L = lauxlib.luaL_newstate();
48 | if (!L) throw Error("failed to create lua state");
49 |
50 | let luaCode = `
51 | local a = true
52 | return a.yo
53 | `;
54 | {
55 | lualib.luaL_openlibs(L);
56 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
57 | lua.lua_pcall(L, 0, -1, 0);
58 | }
59 |
60 | expect(lua.lua_tojsstring(L, -1))
61 | .toMatch("attempt to index a boolean value (local 'a')");
62 | });
63 |
64 |
65 | test('luaG_typeerror', () => {
66 | let L = lauxlib.luaL_newstate();
67 | if (!L) throw Error("failed to create lua state");
68 |
69 | let luaCode = `
70 | local a = true
71 | a.yo = 1
72 | `;
73 | {
74 | lualib.luaL_openlibs(L);
75 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
76 | lua.lua_pcall(L, 0, -1, 0);
77 | }
78 |
79 | expect(lua.lua_tojsstring(L, -1))
80 | .toMatch("attempt to index a boolean value (local 'a')");
81 | });
82 |
83 |
84 | test('luaG_concaterror', () => {
85 | let L = lauxlib.luaL_newstate();
86 | if (!L) throw Error("failed to create lua state");
87 |
88 | let luaCode = `
89 | return {} .. 'hello'
90 | `;
91 | {
92 | lualib.luaL_openlibs(L);
93 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
94 | lua.lua_pcall(L, 0, -1, 0);
95 | }
96 |
97 | expect(lua.lua_tojsstring(L, -1))
98 | .toMatch("attempt to concatenate a table value");
99 | });
100 |
101 |
102 | test('luaG_opinterror', () => {
103 | let L = lauxlib.luaL_newstate();
104 | if (!L) throw Error("failed to create lua state");
105 |
106 | let luaCode = `
107 | return {} + 'hello'
108 | `;
109 | {
110 | lualib.luaL_openlibs(L);
111 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
112 | lua.lua_pcall(L, 0, -1, 0);
113 | }
114 |
115 | expect(lua.lua_tojsstring(L, -1))
116 | .toMatch("attempt to perform arithmetic on a table value");
117 | });
118 |
119 |
120 | test('luaG_tointerror', () => {
121 | let L = lauxlib.luaL_newstate();
122 | if (!L) throw Error("failed to create lua state");
123 |
124 | let luaCode = `
125 | return 123.5 & 12
126 | `;
127 | {
128 | lualib.luaL_openlibs(L);
129 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
130 | lua.lua_pcall(L, 0, -1, 0);
131 | }
132 |
133 | expect(lua.lua_tojsstring(L, -1))
134 | .toMatch("number has no integer representation");
135 | });
136 |
--------------------------------------------------------------------------------
/test/lib-hello.js.mod:
--------------------------------------------------------------------------------
1 | const lua = require('../src/lua.js');
2 |
3 | module.exports = {
4 | "hello": function(L) {
5 | lua.lua_pushliteral(L, "hello from js lib");
6 | return 1;
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/test/lmathlib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | test('math.abs, math.sin, math.cos, math.tan, math.asin, math.acos, math.atan', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | return math.abs(-10), math.abs(-10.5), math.cos(10), math.tan(10),
14 | math.asin(1), math.acos(0.5), math.atan(10)
15 | `;
16 | {
17 | lualib.luaL_openlibs(L);
18 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
19 | lua.lua_call(L, 0, -1);
20 | }
21 |
22 | expect(lua.lua_tointeger(L, -7)).toBe(10);
23 | expect(lua.lua_tonumber(L, -6)).toBe(10.5);
24 | expect(lua.lua_tonumber(L, -5)).toBe(-0.8390715290764524);
25 | expect(lua.lua_tonumber(L, -4)).toBe(0.6483608274590866);
26 | expect(lua.lua_tonumber(L, -3)).toBe(1.5707963267948966);
27 | expect(lua.lua_tonumber(L, -2)).toBe(1.0471975511965979);
28 | expect(lua.lua_tonumber(L, -1)).toBe(1.4711276743037347);
29 | });
30 |
31 |
32 | test('math.ceil, math.floor', () => {
33 | let L = lauxlib.luaL_newstate();
34 | if (!L) throw Error("failed to create lua state");
35 |
36 | let luaCode = `
37 | return math.ceil(10.5), math.floor(10.5)
38 | `;
39 | {
40 | lualib.luaL_openlibs(L);
41 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
42 | lua.lua_call(L, 0, -1);
43 | }
44 |
45 | expect(lua.lua_tointeger(L, -2)).toBe(11);
46 | expect(lua.lua_tointeger(L, -1)).toBe(10);
47 | });
48 |
49 |
50 | test('math.deg, math.rad', () => {
51 | let L = lauxlib.luaL_newstate();
52 | if (!L) throw Error("failed to create lua state");
53 |
54 | let luaCode = `
55 | return math.deg(10), math.rad(10)
56 | `;
57 | {
58 | lualib.luaL_openlibs(L);
59 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
60 | lua.lua_call(L, 0, -1);
61 | }
62 |
63 | expect(lua.lua_tonumber(L, -2)).toBe(572.9577951308232);
64 | expect(lua.lua_tonumber(L, -1)).toBe(0.17453292519943295);
65 | });
66 |
67 |
68 | test('math.log', () => {
69 | let L = lauxlib.luaL_newstate();
70 | if (!L) throw Error("failed to create lua state");
71 |
72 | let luaCode = `
73 | return math.log(10), math.log(10, 2), math.log(10, 10)
74 | `;
75 | {
76 | lualib.luaL_openlibs(L);
77 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
78 | lua.lua_call(L, 0, -1);
79 | }
80 |
81 | expect(lua.lua_tonumber(L, -3)).toBe(2.302585092994046);
82 | expect(lua.lua_tonumber(L, -2)).toBe(3.321928094887362);
83 | expect(lua.lua_tonumber(L, -1)).toBe(1);
84 | });
85 |
86 |
87 | /* Node.js 6 has incorrect results for Math.exp */
88 | (parseInt(process.versions.node) > 6 ? test : test.skip)('math.exp', () => {
89 | let L = lauxlib.luaL_newstate();
90 | if (!L) throw Error("failed to create lua state");
91 |
92 | let luaCode = `
93 | return math.exp(10)
94 | `;
95 | {
96 | lualib.luaL_openlibs(L);
97 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
98 | lua.lua_call(L, 0, -1);
99 | }
100 |
101 | expect(lua.lua_tonumber(L, -1)).toBe(22026.465794806718);
102 | });
103 |
104 |
105 | test('math.min, math.max', () => {
106 | let L = lauxlib.luaL_newstate();
107 | if (!L) throw Error("failed to create lua state");
108 |
109 | let luaCode = `
110 | return math.max(10, 5, 23), math.min(10, 5, 23)
111 | `;
112 | {
113 | lualib.luaL_openlibs(L);
114 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
115 | lua.lua_call(L, 0, -1);
116 | }
117 |
118 | expect(lua.lua_tonumber(L, -2)).toBe(23);
119 | expect(lua.lua_tonumber(L, -1)).toBe(5);
120 | });
121 |
122 |
123 | test('math.random', () => {
124 | let L = lauxlib.luaL_newstate();
125 | if (!L) throw Error("failed to create lua state");
126 |
127 | let luaCode = `
128 | return math.random(), math.random(10, 15)
129 | `;
130 | {
131 | lualib.luaL_openlibs(L);
132 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
133 | lua.lua_call(L, 0, -1);
134 | }
135 |
136 | {
137 | let r = expect(lua.lua_tonumber(L, -2));
138 | r.toBeGreaterThanOrEqual(0);
139 | r.toBeLessThanOrEqual(1);
140 | }
141 | {
142 | let r = expect(lua.lua_tonumber(L, -1));
143 | r.toBeGreaterThanOrEqual(10);
144 | r.toBeLessThanOrEqual(15);
145 | }
146 | });
147 |
148 |
149 | test('math.sqrt', () => {
150 | let L = lauxlib.luaL_newstate();
151 | if (!L) throw Error("failed to create lua state");
152 |
153 | let luaCode = `
154 | return math.sqrt(10)
155 | `;
156 | {
157 | lualib.luaL_openlibs(L);
158 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
159 | lua.lua_call(L, 0, -1);
160 | }
161 |
162 | expect(lua.lua_tonumber(L, -1)).toBe(3.1622776601683795);
163 | });
164 |
165 |
166 | test('math.tointeger', () => {
167 | let L = lauxlib.luaL_newstate();
168 | if (!L) throw Error("failed to create lua state");
169 |
170 | let luaCode = `
171 | return math.tointeger('10')
172 | `;
173 | {
174 | lualib.luaL_openlibs(L);
175 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
176 | lua.lua_call(L, 0, -1);
177 | }
178 |
179 | expect(lua.lua_tonumber(L, -1)).toBe(10);
180 | });
181 |
182 |
183 | test('math.type', () => {
184 | let L = lauxlib.luaL_newstate();
185 | if (!L) throw Error("failed to create lua state");
186 |
187 | let luaCode = `
188 | return math.type(10), math.type(10.5), math.type('hello')
189 | `;
190 | {
191 | lualib.luaL_openlibs(L);
192 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
193 | lua.lua_call(L, 0, -1);
194 | }
195 |
196 | expect(lua.lua_tojsstring(L, -3)).toBe("integer");
197 | expect(lua.lua_tojsstring(L, -2)).toBe("float");
198 | expect(lua.lua_tojsstring(L, -1)).toBe(null);
199 | });
200 |
201 |
202 | test('math.ult', () => {
203 | let L = lauxlib.luaL_newstate();
204 | if (!L) throw Error("failed to create lua state");
205 |
206 | let luaCode = `
207 | return math.ult(5, 200)
208 | `;
209 | {
210 | lualib.luaL_openlibs(L);
211 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
212 | lua.lua_call(L, 0, -1);
213 | }
214 |
215 | expect(lua.lua_toboolean(L, -1)).toBe(true);
216 | });
217 |
218 |
219 | test('math.fmod', () => {
220 | let L = lauxlib.luaL_newstate();
221 | if (!L) throw Error("failed to create lua state");
222 |
223 | let luaCode = `
224 | return math.fmod(2,5)
225 | `;
226 | {
227 | lualib.luaL_openlibs(L);
228 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
229 | lua.lua_call(L, 0, -1);
230 | }
231 |
232 | expect(lua.lua_tonumber(L, -1)).toBe(2);
233 | });
234 |
235 |
236 | test('math.modf', () => {
237 | let L = lauxlib.luaL_newstate();
238 | if (!L) throw Error("failed to create lua state");
239 |
240 | let luaCode = `
241 | return math.modf(3.4, 0.6)
242 | `;
243 | {
244 | lualib.luaL_openlibs(L);
245 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
246 | lua.lua_call(L, 0, -1);
247 | }
248 |
249 | expect(lua.lua_tonumber(L, -2)).toBe(3);
250 | expect(lua.lua_tonumber(L, -1)).toBe(0.3999999999999999);
251 | });
252 |
--------------------------------------------------------------------------------
/test/load.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {toByteCode} = require("./tests.js");
4 |
5 | const lua = require('../src/lua.js');
6 | const lauxlib = require('../src/lauxlib.js');
7 | const lualib = require('../src/lualib.js');
8 | const {to_luastring} = require("../src/fengaricore.js");
9 |
10 | test('luaL_loadstring', () => {
11 | let L = lauxlib.luaL_newstate();
12 | if (!L) throw Error("failed to create lua state");
13 |
14 | let luaCode = `
15 | local a = "hello world"
16 | return a
17 | `;
18 | {
19 | lualib.luaL_openlibs(L);
20 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
21 | lua.lua_call(L, 0, -1);
22 | }
23 | expect(lua.lua_tojsstring(L, -1))
24 | .toBe("hello world");
25 | });
26 |
27 |
28 | test('load', () => {
29 | let L = lauxlib.luaL_newstate();
30 | if (!L) throw Error("failed to create lua state");
31 |
32 | let luaCode = `
33 | local f = load("return 'js running lua running lua'")
34 | return f()
35 | `;
36 | {
37 | lualib.luaL_openlibs(L);
38 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
39 | lua.lua_call(L, 0, -1);
40 | }
41 | expect(lua.lua_tojsstring(L, -1))
42 | .toBe("js running lua running lua");
43 | });
44 |
45 |
46 | test('undump empty string', () => {
47 | let L = lauxlib.luaL_newstate();
48 | if (!L) throw Error("failed to create lua state");
49 |
50 | let luaCode = `
51 | assert(load(string.dump(function()
52 | local str = ""
53 | return #str -- something that inspects the string
54 | end)))()
55 | `;
56 | {
57 | lualib.luaL_openlibs(L);
58 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
59 | lua.lua_call(L, 0, 0);
60 | }
61 | });
62 |
63 |
64 | test('luaL_loadbuffer', () => {
65 | let L = lauxlib.luaL_newstate();
66 | if (!L) throw Error("failed to create lua state");
67 |
68 | let luaCode = `
69 | local a = "hello world"
70 | return a
71 | `;
72 | {
73 | lualib.luaL_openlibs(L);
74 | let bc = toByteCode(luaCode);
75 | lauxlib.luaL_loadbuffer(L, bc, null, to_luastring("test"));
76 | lua.lua_call(L, 0, -1);
77 | }
78 | expect(lua.lua_tojsstring(L, -1))
79 | .toBe("hello world");
80 | });
81 |
82 | // TODO: test stdin
83 | test('loadfile', () => {
84 | let L = lauxlib.luaL_newstate();
85 | if (!L) throw Error("failed to create lua state");
86 |
87 | let luaCode = `
88 | local f = assert(loadfile("test/loadfile-test.lua"))
89 | return f()
90 | `;
91 | {
92 | lualib.luaL_openlibs(L);
93 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
94 | lua.lua_call(L, 0, -1);
95 | }
96 | expect(lua.lua_tojsstring(L, -1))
97 | .toBe("hello world");
98 | });
99 |
100 |
101 | test('loadfile (binary)', () => {
102 | let L = lauxlib.luaL_newstate();
103 | if (!L) throw Error("failed to create lua state");
104 |
105 | let luaCode = `
106 | local f = assert(loadfile("test/loadfile-test.bc"))
107 | return f()
108 | `;
109 | {
110 | lualib.luaL_openlibs(L);
111 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
112 | lua.lua_call(L, 0, -1);
113 | }
114 | expect(lua.lua_tojsstring(L, -1))
115 | .toBe("hello world");
116 | });
117 |
118 |
119 | test('dofile', () => {
120 | let L = lauxlib.luaL_newstate();
121 | if (!L) throw Error("failed to create lua state");
122 |
123 | let luaCode = `
124 | return dofile("test/loadfile-test.lua")
125 | `;
126 | {
127 | lualib.luaL_openlibs(L);
128 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
129 | lua.lua_call(L, 0, -1);
130 | }
131 | expect(lua.lua_tojsstring(L, -1))
132 | .toBe("hello world");
133 | });
134 |
--------------------------------------------------------------------------------
/test/loadfile-test.bc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengari-lua/fengari/bd945f0ef42caad86aff59365fcb8a3ec8509d06/test/loadfile-test.bc
--------------------------------------------------------------------------------
/test/loadfile-test.lua:
--------------------------------------------------------------------------------
1 | return "hello world"
--------------------------------------------------------------------------------
/test/loadlib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | test('require an existing module', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | return require('os')
14 | `;
15 | {
16 | lualib.luaL_openlibs(L);
17 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
18 | lua.lua_call(L, 0, -1);
19 | }
20 |
21 | expect(lua.lua_istable(L, -1)).toBe(true);
22 | });
23 |
24 |
25 | test('require a file', () => {
26 | let L = lauxlib.luaL_newstate();
27 | if (!L) throw Error("failed to create lua state");
28 |
29 | let luaCode = `
30 | return require('test.module-hello')()
31 | `;
32 | {
33 | lualib.luaL_openlibs(L);
34 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
35 | lua.lua_call(L, 0, -1);
36 | }
37 |
38 | expect(lua.lua_tojsstring(L, -1)).toBe("hello from module");
39 | });
40 |
41 |
42 | test('package.loadlib', () => {
43 | let L = lauxlib.luaL_newstate();
44 | if (!L) throw Error("failed to create lua state");
45 |
46 | let luaCode = `
47 | return package.loadlib('./test/lib-hello.js.mod', 'hello')()
48 | `;
49 | {
50 | lualib.luaL_openlibs(L);
51 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
52 | lua.lua_call(L, 0, -1);
53 | }
54 |
55 | expect(lua.lua_tojsstring(L, -1)).toBe("hello from js lib");
56 | });
57 |
58 |
59 | test('package.searchpath', () => {
60 | let L = lauxlib.luaL_newstate();
61 | if (!L) throw Error("failed to create lua state");
62 |
63 | let luaCode = `
64 | return package.searchpath('module-hello', './?.lua;./test/?.lua')
65 | `;
66 | {
67 | lualib.luaL_openlibs(L);
68 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
69 | lua.lua_call(L, 0, -1);
70 | }
71 |
72 | expect(lua.lua_tojsstring(L, -1)).toBe("./test/module-hello.lua");
73 | });
74 |
--------------------------------------------------------------------------------
/test/loslib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | test('os.time', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | return os.time()
14 | `;
15 | {
16 | lualib.luaL_openlibs(L);
17 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
18 | lua.lua_call(L, 0, -1);
19 | }
20 |
21 | expect(lua.lua_isinteger(L, -1)).toBe(true);
22 | });
23 |
24 |
25 | test('os.time (with format)', () => {
26 | let L = lauxlib.luaL_newstate();
27 | if (!L) throw Error("failed to create lua state");
28 |
29 | let luaCode = `
30 | return os.time({
31 | day = 8,
32 | month = 2,
33 | year = 2015
34 | })
35 | `;
36 | {
37 | lualib.luaL_openlibs(L);
38 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
39 | lua.lua_call(L, 0, -1);
40 | }
41 |
42 | expect(lua.lua_tointeger(L, -1))
43 | .toBe(new Date(2015, 1, 8, 12, 0, 0, 0).getTime() / 1000);
44 | });
45 |
46 |
47 | test('os.difftime', () => {
48 | let L = lauxlib.luaL_newstate();
49 | if (!L) throw Error("failed to create lua state");
50 |
51 | let luaCode = `
52 | local t1 = os.time()
53 | local t2 = os.time()
54 | return os.difftime(t2, t1)
55 | `;
56 | {
57 | lualib.luaL_openlibs(L);
58 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
59 | lua.lua_call(L, 0, -1);
60 | }
61 |
62 | expect(lua.lua_isnumber(L, -1)).toBe(true);
63 | });
64 |
65 |
66 | test('os.date', () => {
67 | let L = lauxlib.luaL_newstate();
68 | if (!L) throw Error("failed to create lua state");
69 |
70 | let luaCode = `
71 | return os.date('%Y-%m-%d', os.time({
72 | day = 8,
73 | month = 2,
74 | year = 2015
75 | }))
76 | `;
77 | {
78 | lualib.luaL_openlibs(L);
79 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
80 | lua.lua_call(L, 0, -1);
81 | }
82 |
83 | expect(lua.lua_tojsstring(L, -1)).toBe("2015-02-08");
84 | });
85 |
86 |
87 | test('os.date normalisation', () => {
88 | let L = lauxlib.luaL_newstate();
89 | if (!L) throw Error("failed to create lua state");
90 |
91 | let luaCode = `
92 | return os.date('%Y-%m-%d', os.time({
93 | day = 0,
94 | month = 0,
95 | year = 2014
96 | }))
97 | `;
98 | {
99 | lualib.luaL_openlibs(L);
100 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
101 | lua.lua_call(L, 0, -1);
102 | }
103 |
104 | expect(lua.lua_tojsstring(L, -1)).toBe("2013-11-30");
105 | });
106 |
107 |
108 | test('os.time normalisation of table', () => {
109 | let L = lauxlib.luaL_newstate();
110 | if (!L) throw Error("failed to create lua state");
111 |
112 | let luaCode = `
113 | local t = {
114 | day = 20,
115 | month = 2,
116 | year = 2018
117 | }
118 | os.time(t)
119 | assert(t.day == 20, "unmodified day")
120 | assert(t.month == 2, "unmodified month")
121 | assert(t.year == 2018, "unmodified year")
122 | assert(t.wday == 3, "correct wday")
123 | assert(t.yday == 51, "correct yday")
124 | `;
125 | lualib.luaL_openlibs(L);
126 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
127 | lua.lua_call(L, 0, 0);
128 | });
129 |
130 |
131 | test('os.setlocale', () => {
132 | let L = lauxlib.luaL_newstate();
133 | if (!L) throw Error("failed to create lua state");
134 |
135 | let luaCode = `
136 | assert("C" == os.setlocale())
137 | assert("C" == os.setlocale(""))
138 | assert("C" == os.setlocale("C"))
139 | assert("C" == os.setlocale("POSIX"))
140 | assert(nil == os.setlocale("any_other_locale"))
141 | `;
142 | lualib.luaL_openlibs(L);
143 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
144 | lua.lua_call(L, 0, 0);
145 | });
146 |
147 |
148 | test('os.getenv', () => {
149 | let L = lauxlib.luaL_newstate();
150 | if (!L) throw Error("failed to create lua state");
151 |
152 | let luaCode = `
153 | return os.getenv('PATH')
154 | `;
155 | {
156 | lualib.luaL_openlibs(L);
157 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
158 | lua.lua_call(L, 0, -1);
159 | }
160 |
161 | expect(lua.lua_isstring(L, -1)).toBe(true);
162 | });
163 |
--------------------------------------------------------------------------------
/test/ltablib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | const inttable2array = function(t) {
9 | let a = [];
10 | t.strong.forEach(function (v, k) {
11 | if (v.key.ttisnumber())
12 | a[k - 1] = v.value;
13 | });
14 | return a.map(e => e.value);
15 | };
16 |
17 | test('table.concat', () => {
18 | let L = lauxlib.luaL_newstate();
19 | if (!L) throw Error("failed to create lua state");
20 |
21 | let luaCode = `
22 | return table.concat({1, 2, 3, 4, 5, 6, 7}, ",", 3, 5)
23 | `;
24 | {
25 | lualib.luaL_openlibs(L);
26 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
27 | lua.lua_call(L, 0, -1);
28 | }
29 |
30 | expect(lua.lua_tojsstring(L, -1)).toBe("3,4,5");
31 | });
32 |
33 |
34 | test('table.pack', () => {
35 | let L = lauxlib.luaL_newstate();
36 | if (!L) throw Error("failed to create lua state");
37 |
38 | let luaCode = `
39 | return table.pack(1, 2, 3)
40 | `;
41 | {
42 | lualib.luaL_openlibs(L);
43 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
44 | lua.lua_call(L, 0, -1);
45 | }
46 |
47 | expect([...lua.lua_topointer(L, -1).strong.entries()]
48 | .filter(e => e[1].key.ttisnumber()) // Filter out the 'n' field
49 | .map(e => e[1].value.value).reverse()
50 | ).toEqual([1, 2, 3]);
51 | });
52 |
53 |
54 | test('table.unpack', () => {
55 | let L = lauxlib.luaL_newstate();
56 | if (!L) throw Error("failed to create lua state");
57 |
58 | let luaCode = `
59 | return table.unpack({1, 2, 3, 4, 5}, 2, 4)
60 | `;
61 | {
62 | lualib.luaL_openlibs(L);
63 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
64 | lua.lua_call(L, 0, -1);
65 | }
66 |
67 | expect(lua.lua_tointeger(L, -3)).toBe(2);
68 | expect(lua.lua_tointeger(L, -2)).toBe(3);
69 | expect(lua.lua_tointeger(L, -1)).toBe(4);
70 | });
71 |
72 |
73 | test('table.insert', () => {
74 | let L = lauxlib.luaL_newstate();
75 | if (!L) throw Error("failed to create lua state");
76 |
77 | let luaCode = `
78 | local t = {1, 3, 4}
79 | table.insert(t, 5)
80 | table.insert(t, 2, 2)
81 | return t
82 | `;
83 | {
84 | lualib.luaL_openlibs(L);
85 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
86 | lua.lua_call(L, 0, -1);
87 | }
88 |
89 | expect(
90 | [...lua.lua_topointer(L, -1).strong.entries()]
91 | .filter(e => e[1].key.ttisnumber())
92 | .map(e => e[1].value.value).sort()
93 | ).toEqual([1, 2, 3, 4, 5]);
94 | });
95 |
96 |
97 | test('table.remove', () => {
98 | let L = lauxlib.luaL_newstate();
99 | if (!L) throw Error("failed to create lua state");
100 |
101 | let luaCode = `
102 | local t = {1, 2, 3, 3, 4, 4}
103 | table.remove(t)
104 | table.remove(t, 3)
105 | return t
106 | `;
107 | {
108 | lualib.luaL_openlibs(L);
109 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
110 | lua.lua_call(L, 0, -1);
111 | }
112 |
113 | expect(
114 | [...lua.lua_topointer(L, -1).strong.entries()]
115 | .filter(e => e[1].key.ttisnumber())
116 | .map(e => e[1].value.value).sort()
117 | ).toEqual([1, 2, 3, 4]);
118 | });
119 |
120 |
121 | test('table.move', () => {
122 | let L = lauxlib.luaL_newstate();
123 | if (!L) throw Error("failed to create lua state");
124 |
125 | let luaCode = `
126 | local t1 = {3, 4, 5}
127 | local t2 = {1, 2, nil, nil, nil, 6}
128 | return table.move(t1, 1, #t1, 3, t2)
129 | `;
130 | {
131 | lualib.luaL_openlibs(L);
132 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
133 | lua.lua_call(L, 0, -1);
134 | }
135 |
136 | expect(
137 | [...lua.lua_topointer(L, -1).strong.entries()]
138 | .filter(e => e[1].key.ttisnumber())
139 | .map(e => e[1].value.value).sort()
140 | ).toEqual([1, 2, 3, 4, 5, 6]);
141 | });
142 |
143 |
144 | test('table.sort (<)', () => {
145 | let L = lauxlib.luaL_newstate();
146 | if (!L) throw Error("failed to create lua state");
147 |
148 | let luaCode = `
149 | local t = {3, 1, 5, ['just'] = 'tofuckitup', 2, 4}
150 | table.sort(t)
151 | return t
152 | `;
153 | {
154 | lualib.luaL_openlibs(L);
155 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
156 | lua.lua_call(L, 0, -1);
157 | }
158 |
159 | expect(inttable2array(lua.lua_topointer(L, -1)))
160 | .toEqual([1, 2, 3, 4, 5]);
161 | });
162 |
163 |
164 | test('table.sort with cmp function', () => {
165 | let L = lauxlib.luaL_newstate();
166 | if (!L) throw Error("failed to create lua state");
167 |
168 | let luaCode = `
169 | local t = {3, 1, 5, ['just'] = 'tofuckitup', 2, 4}
170 | table.sort(t, function (a, b)
171 | return a > b
172 | end)
173 | return t
174 | `;
175 | {
176 | lualib.luaL_openlibs(L);
177 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
178 | lua.lua_call(L, 0, -1);
179 | }
180 |
181 | expect(inttable2array(lua.lua_topointer(L, -1)))
182 | .toEqual([5, 4, 3, 2, 1]);
183 | });
184 |
--------------------------------------------------------------------------------
/test/lua.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../src/lua.js');
4 | const lauxlib = require('../src/lauxlib.js');
5 | const lualib = require('../src/lualib.js');
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | // TODO: remove
9 | test.skip('locals.lua', () => {
10 | let L = lauxlib.luaL_newstate();
11 | if (!L) throw Error("failed to create lua state");
12 |
13 | let luaCode = `
14 | _soft = true
15 | require = function(lib) return _G[lib] end -- NYI
16 | return dofile("tests/lua-tests/locals.lua")
17 | `;
18 | {
19 | lualib.luaL_openlibs(L);
20 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
21 | lua.lua_call(L, 0, -1);
22 | }
23 | });
24 |
25 |
26 | test.skip('constructs.lua', () => {
27 | let L = lauxlib.luaL_newstate();
28 | if (!L) throw Error("failed to create lua state");
29 |
30 | let luaCode = `
31 | _soft = true
32 | require = function(lib) return _G[lib] end -- NYI
33 | return dofile("tests/lua-tests/constructs.lua")
34 | `;
35 | {
36 | lualib.luaL_openlibs(L);
37 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
38 | lua.lua_call(L, 0, -1);
39 | }
40 | });
41 |
42 |
43 | test.skip('strings.lua', () => {
44 | let L = lauxlib.luaL_newstate();
45 | if (!L) throw Error("failed to create lua state");
46 |
47 | let luaCode = `
48 | return dofile("tests/lua-tests/strings.lua")
49 | `;
50 | {
51 | lualib.luaL_openlibs(L);
52 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
53 | lua.lua_call(L, 0, -1);
54 | }
55 | });
56 |
57 |
58 | test('__newindex leaves nils', () => {
59 | let L = lauxlib.luaL_newstate();
60 | if (!L) throw Error("failed to create lua state");
61 |
62 | let luaCode = `
63 | local x = setmetatable({}, {
64 | __newindex = function(t,k,v)
65 | rawset(t,'_'..k,v)
66 | end
67 | })
68 | x.test = 4
69 | for k,v in pairs(x) do
70 | assert(k ~= "test", "found phantom key")
71 | end
72 | `;
73 | {
74 | lualib.luaL_openlibs(L);
75 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
76 | lua.lua_call(L, 0, -1);
77 | }
78 | });
79 |
--------------------------------------------------------------------------------
/test/lutf8lib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require("../src/lua.js");
4 | const lauxlib = require("../src/lauxlib.js");
5 | const lualib = require("../src/lualib.js");
6 | const {to_luastring} = require("../src/fengaricore.js");
7 |
8 | test('utf8.offset', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | return utf8.offset("( ͡° ͜ʖ ͡° )", 5)
14 | `;
15 | {
16 | lualib.luaL_openlibs(L);
17 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
18 | lua.lua_call(L, 0, -1);
19 | }
20 |
21 | expect(lua.lua_tointeger(L, -1)).toBe(7);
22 | });
23 |
24 |
25 | test('utf8.codepoint', () => {
26 | let L = lauxlib.luaL_newstate();
27 | if (!L) throw Error("failed to create lua state");
28 |
29 | let luaCode = `
30 | return utf8.codepoint("( ͡° ͜ʖ ͡° )", 5, 8)
31 | `;
32 | {
33 | lualib.luaL_openlibs(L);
34 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
35 | lua.lua_call(L, 0, -1);
36 | }
37 |
38 | expect(lua.lua_tointeger(L, -3)).toBe(176);
39 | expect(lua.lua_tointeger(L, -2)).toBe(32);
40 | expect(lua.lua_tointeger(L, -1)).toBe(860);
41 | });
42 |
43 |
44 | test('utf8.char', () => {
45 | let L = lauxlib.luaL_newstate();
46 | if (!L) throw Error("failed to create lua state");
47 |
48 | let luaCode = `
49 | return utf8.char(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 32, 41)
50 | `;
51 | {
52 | lualib.luaL_openlibs(L);
53 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
54 | lua.lua_call(L, 0, -1);
55 | }
56 |
57 | expect(lua.lua_tojsstring(L, -1)).toBe("( ͡° ͜ʖ ͡° )");
58 | });
59 |
60 |
61 | test('utf8.len', () => {
62 | let L = lauxlib.luaL_newstate();
63 | if (!L) throw Error("failed to create lua state");
64 |
65 | let luaCode = `
66 | return utf8.len("( ͡° ͜ʖ ͡° )")
67 | `;
68 | {
69 | lualib.luaL_openlibs(L);
70 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
71 | lua.lua_call(L, 0, -1);
72 | }
73 |
74 | expect(lua.lua_tointeger(L, -1)).toBe(12);
75 | });
76 |
77 |
78 | test('utf8.codes', () => {
79 | let L = lauxlib.luaL_newstate();
80 | if (!L) throw Error("failed to create lua state");
81 |
82 | let luaCode = `
83 | local s = "( ͡° ͜ʖ ͡° )"
84 | local results = ""
85 | for p, c in utf8.codes(s) do
86 | results = results .. "[" .. p .. "," .. c .. "] "
87 | end
88 | return results
89 | `;
90 | {
91 | lualib.luaL_openlibs(L);
92 | expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
93 | lua.lua_call(L, 0, -1);
94 | }
95 |
96 | expect(lua.lua_tojsstring(L, -1)).toBe("[1,40] [2,32] [3,865] [5,176] [7,32] [8,860] [10,662] [12,32] [13,865] [15,176] [17,32] [18,41] ");
97 | });
98 |
--------------------------------------------------------------------------------
/test/manual-tests/debug-cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | "use strict";
3 |
4 | const lua = require('../../src/lua.js');
5 | const lauxlib = require('../../src/lauxlib.js');
6 | const lualib = require('../../src/lualib.js');
7 | const {to_luastring} = require("../../src/fengaricore.js");
8 |
9 | let luaCode = `
10 | a = "debug me"
11 | debug.debug()
12 | `, L;
13 |
14 | L = lauxlib.luaL_newstate();
15 |
16 | lualib.luaL_openlibs(L);
17 |
18 | lauxlib.luaL_loadstring(L, to_luastring(luaCode));
19 |
20 | lua.lua_call(L, 0, 0);
21 |
--------------------------------------------------------------------------------
/test/module-hello.lua:
--------------------------------------------------------------------------------
1 | return function()
2 | return 'hello from module'
3 | end
4 |
--------------------------------------------------------------------------------
/test/test-suite/attrib.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../../src/lua.js');
4 | const lauxlib = require('../../src/lauxlib.js');
5 | const lualib = require('../../src/lualib.js');
6 | const {to_luastring} = require("../../src/fengaricore.js");
7 |
8 | test("[test-suite] attrib: testing require", () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | assert(require"string" == string)
14 | assert(require"math" == math)
15 | assert(require"table" == table)
16 | assert(require"io" == io)
17 | assert(require"os" == os)
18 | assert(require"coroutine" == coroutine)
19 |
20 | assert(type(package.path) == "string")
21 | assert(type(package.jspath) == "string")
22 | assert(type(package.loaded) == "table")
23 | assert(type(package.preload) == "table")
24 |
25 | assert(type(package.config) == "string")
26 | -- print("package config: "..string.gsub(package.config, "\\n", "|"))
27 |
28 | do
29 | -- create a path with 'max' templates,
30 | -- each with 1-10 repetitions of '?'
31 | local max = _soft and 100 or 2000
32 | local t = {}
33 | for i = 1,max do t[i] = string.rep("?", i%10 + 1) end
34 | t[#t + 1] = ";" -- empty template
35 | local path = table.concat(t, ";")
36 | -- use that path in a search
37 | local s, err = package.searchpath("xuxu", path)
38 | -- search fails; check that message has an occurence of
39 | -- '??????????' with ? replaced by xuxu and at least 'max' lines
40 | assert(not s and
41 | string.find(err, string.rep("xuxu", 10)) and
42 | #string.gsub(err, "[^\\n]", "") >= max)
43 | -- path with one very long template
44 | local path = string.rep("?", max)
45 | local s, err = package.searchpath("xuxu", path)
46 | assert(not s and string.find(err, string.rep('xuxu', max)))
47 | end
48 |
49 | do
50 | local oldpath = package.path
51 | package.path = {}
52 | local s, err = pcall(require, "no-such-file")
53 | assert(not s and string.find(err, "package.path"))
54 | package.path = oldpath
55 | end
56 | `;
57 | lualib.luaL_openlibs(L);
58 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
59 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
60 | lua.lua_call(L, 0, 0);
61 | });
62 |
63 |
64 | // TODO: when io.write etc.
65 | test.skip("[test-suite] attrib: system specific tests for 'require'", () => {
66 | let L = lauxlib.luaL_newstate();
67 | if (!L) throw Error("failed to create lua state");
68 | });
69 |
70 |
71 | test("[test-suite] attrib: testing assignments, logical operators, and constructors", () => {
72 | let L = lauxlib.luaL_newstate();
73 | if (!L) throw Error("failed to create lua state");
74 |
75 | let luaCode = `
76 | local res, res2 = 27
77 |
78 | a, b = 1, 2+3
79 | assert(a==1 and b==5)
80 | a={}
81 | function f() return 10, 11, 12 end
82 | a.x, b, a[1] = 1, 2, f()
83 | assert(a.x==1 and b==2 and a[1]==10)
84 | a[f()], b, a[f()+3] = f(), a, 'x'
85 | assert(a[10] == 10 and b == a and a[13] == 'x')
86 |
87 | do
88 | local f = function (n) local x = {}; for i=1,n do x[i]=i end;
89 | return table.unpack(x) end;
90 | local a,b,c
91 | a,b = 0, f(1)
92 | assert(a == 0 and b == 1)
93 | A,b = 0, f(1)
94 | assert(A == 0 and b == 1)
95 | a,b,c = 0,5,f(4)
96 | assert(a==0 and b==5 and c==1)
97 | a,b,c = 0,5,f(0)
98 | assert(a==0 and b==5 and c==nil)
99 | end
100 |
101 | a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
102 | assert(not a and b and c and d==6)
103 |
104 | d = 20
105 | a, b, c, d = f()
106 | assert(a==10 and b==11 and c==12 and d==nil)
107 | a,b = f(), 1, 2, 3, f()
108 | assert(a==10 and b==1)
109 |
110 | assert(ab == true)
111 | assert((10 and 2) == 2)
112 | assert((10 or 2) == 10)
113 | assert((10 or assert(nil)) == 10)
114 | assert(not (nil and assert(nil)))
115 | assert((nil or "alo") == "alo")
116 | assert((nil and 10) == nil)
117 | assert((false and 10) == false)
118 | assert((true or 10) == true)
119 | assert((false or 10) == 10)
120 | assert(false ~= nil)
121 | assert(nil ~= false)
122 | assert(not nil == true)
123 | assert(not not nil == false)
124 | assert(not not 1 == true)
125 | assert(not not a == true)
126 | assert(not not (6 or nil) == true)
127 | assert(not not (nil and 56) == false)
128 | assert(not not (nil and true) == false)
129 | assert(not 10 == false)
130 | assert(not {} == false)
131 | assert(not 0.5 == false)
132 | assert(not "x" == false)
133 |
134 | assert({} ~= {})
135 |
136 | a = {}
137 | a[true] = 20
138 | a[false] = 10
139 | assert(a[1<2] == 20 and a[1>2] == 10)
140 |
141 | function f(a) return a end
142 |
143 | local a = {}
144 | for i=3000,-3000,-1 do a[i + 0.0] = i; end
145 | a[10e30] = "alo"; a[true] = 10; a[false] = 20
146 | assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10)
147 | for i=3000,-3000,-1 do assert(a[i] == i); end
148 | a[print] = assert
149 | a[f] = print
150 | a[a] = a
151 | assert(a[a][a][a][a][print] == assert)
152 | a[print](a[a[f]] == a[print])
153 | assert(not pcall(function () local a = {}; a[nil] = 10 end))
154 | assert(not pcall(function () local a = {[nil] = 10} end))
155 | assert(a[nil] == nil)
156 | a = nil
157 |
158 | a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
159 | a, a.x, a.y = a, a[-3]
160 | assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
161 | a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2
162 | a[1].alo(a[2]==10 and b==10 and c==print)
163 | `;
164 | lualib.luaL_openlibs(L);
165 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
166 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
167 | lua.lua_call(L, 0, 0);
168 | });
169 |
170 |
171 | test("[test-suite] attrib: test of large float/integer indices ", () => {
172 | let L = lauxlib.luaL_newstate();
173 | if (!L) throw Error("failed to create lua state");
174 |
175 | let luaCode = `
176 | a = {}
177 | a[true] = 20
178 | a[false] = 10
179 | assert(a[1<2] == 20 and a[1>2] == 10)
180 |
181 | function f(a) return a end
182 |
183 | local a = {}
184 | for i=3000,-3000,-1 do a[i + 0.0] = i; end
185 | a[10e30] = "alo"; a[true] = 10; a[false] = 20
186 | assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10)
187 | for i=3000,-3000,-1 do assert(a[i] == i); end
188 | a[print] = assert
189 | a[f] = print
190 | a[a] = a
191 | assert(a[a][a][a][a][print] == assert)
192 | a[print](a[a[f]] == a[print])
193 | assert(not pcall(function () local a = {}; a[nil] = 10 end))
194 | assert(not pcall(function () local a = {[nil] = 10} end))
195 | assert(a[nil] == nil)
196 | a = nil
197 |
198 | a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
199 | a, a.x, a.y = a, a[-3]
200 | assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
201 | a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2
202 | a[1].alo(a[2]==10 and b==10 and c==print)
203 |
204 | -- compute maximum integer where all bits fit in a float
205 | local maxint = math.maxinteger
206 |
207 | while maxint - 1.0 == maxint - 0.0 do -- trim (if needed) to fit in a float
208 | maxint = maxint // 2
209 | end
210 |
211 | maxintF = maxint + 0.0 -- float version
212 |
213 | assert(math.type(maxintF) == "float" and maxintF >= 2.0^14)
214 |
215 | -- floats and integers must index the same places
216 | a[maxintF] = 10; a[maxintF - 1.0] = 11;
217 | a[-maxintF] = 12; a[-maxintF + 1.0] = 13;
218 |
219 | assert(a[maxint] == 10 and a[maxint - 1] == 11 and
220 | a[-maxint] == 12 and a[-maxint + 1] == 13)
221 |
222 | a[maxint] = 20
223 | a[-maxint] = 22
224 |
225 | assert(a[maxintF] == 20 and a[maxintF - 1.0] == 11 and
226 | a[-maxintF] == 22 and a[-maxintF + 1.0] == 13)
227 | `;
228 | lualib.luaL_openlibs(L);
229 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
230 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
231 | lua.lua_call(L, 0, 0);
232 | });
233 |
234 |
235 | test("[test-suite] attrib: test conflicts in multiple assignment", () => {
236 | let L = lauxlib.luaL_newstate();
237 | if (!L) throw Error("failed to create lua state");
238 |
239 | let luaCode = `
240 | do
241 | local a,i,j,b
242 | a = {'a', 'b'}; i=1; j=2; b=a
243 | i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
244 | assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
245 | b[3] == 1)
246 | end
247 | `;
248 | lualib.luaL_openlibs(L);
249 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
250 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
251 | lua.lua_call(L, 0, 0);
252 | });
253 |
254 |
255 | test("[test-suite] attrib: repeat test with upvalues", () => {
256 | let L = lauxlib.luaL_newstate();
257 | if (!L) throw Error("failed to create lua state");
258 |
259 | let luaCode = `
260 | do
261 | local a,i,j,b
262 | a = {'a', 'b'}; i=1; j=2; b=a
263 | local function foo ()
264 | i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
265 | end
266 | foo()
267 | assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
268 | b[3] == 1)
269 | local t = {}
270 | (function (a) t[a], a = 10, 20 end)(1);
271 | assert(t[1] == 10)
272 | end
273 | `;
274 | lualib.luaL_openlibs(L);
275 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
276 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
277 | lua.lua_call(L, 0, 0);
278 | });
279 |
280 |
281 | test("[test-suite] attrib: bug in 5.2 beta", () => {
282 | let L = lauxlib.luaL_newstate();
283 | if (!L) throw Error("failed to create lua state");
284 |
285 | let luaCode = `
286 | local function foo ()
287 | local a
288 | return function ()
289 | local b
290 | a, b = 3, 14 -- local and upvalue have same index
291 | return a, b
292 | end
293 | end
294 |
295 | local a, b = foo()()
296 | assert(a == 3 and b == 14)
297 | `;
298 | lualib.luaL_openlibs(L);
299 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
300 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
301 | lua.lua_call(L, 0, 0);
302 | });
303 |
--------------------------------------------------------------------------------
/test/test-suite/closure.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../../src/lua.js');
4 | const lauxlib = require('../../src/lauxlib.js');
5 | const lualib = require('../../src/lualib.js');
6 | const {to_luastring} = require("../../src/fengaricore.js");
7 |
8 | test("[test-suite] closure: testing equality", () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | a = {}
14 | for i = 1, 5 do a[i] = function (x) return x + a + _ENV end end
15 | assert(a[3] == a[4] and a[4] == a[5])
16 |
17 | for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end
18 | assert(a[3] ~= a[4] and a[4] ~= a[5])
19 |
20 | local function f()
21 | return function (x) return math.sin(_ENV[x]) end
22 | end
23 | assert(f() == f())
24 | `;
25 | lualib.luaL_openlibs(L);
26 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
27 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
28 | lua.lua_call(L, 0, 0);
29 | });
30 |
31 |
32 | test("[test-suite] closure: testing closures with 'for' control variable", () => {
33 | let L = lauxlib.luaL_newstate();
34 | if (!L) throw Error("failed to create lua state");
35 |
36 | let luaCode = `
37 | a = {}
38 | for i=1,10 do
39 | a[i] = {set = function(x) i=x end, get = function () return i end}
40 | if i == 3 then break end
41 | end
42 | assert(a[4] == nil)
43 | a[1].set(10)
44 | assert(a[2].get() == 2)
45 | a[2].set('a')
46 | assert(a[3].get() == 3)
47 | assert(a[2].get() == 'a')
48 |
49 | a = {}
50 | local t = {"a", "b"}
51 | for i = 1, #t do
52 | local k = t[i]
53 | a[i] = {set = function(x, y) i=x; k=y end,
54 | get = function () return i, k end}
55 | if i == 2 then break end
56 | end
57 | a[1].set(10, 20)
58 | local r,s = a[2].get()
59 | assert(r == 2 and s == 'b')
60 | r,s = a[1].get()
61 | assert(r == 10 and s == 20)
62 | a[2].set('a', 'b')
63 | r,s = a[2].get()
64 | assert(r == "a" and s == "b")
65 | `;
66 | lualib.luaL_openlibs(L);
67 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
68 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
69 | lua.lua_call(L, 0, 0);
70 | });
71 |
72 |
73 | test("[test-suite] closure: testing closures with 'for' control variable x break", () => {
74 | let L = lauxlib.luaL_newstate();
75 | if (!L) throw Error("failed to create lua state");
76 |
77 | let luaCode = `
78 | local t = {"a", "b"}
79 |
80 | for i=1,3 do
81 | f = function () return i end
82 | break
83 | end
84 | assert(f() == 1)
85 |
86 | for k = 1, #t do
87 | local v = t[k]
88 | f = function () return k, v end
89 | break
90 | end
91 | assert(({f()})[1] == 1)
92 | assert(({f()})[2] == "a")
93 | `;
94 | lualib.luaL_openlibs(L);
95 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
96 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
97 | lua.lua_call(L, 0, 0);
98 | });
99 |
100 |
101 | test("[test-suite] closure: testing closure x break x return x errors", () => {
102 | let L = lauxlib.luaL_newstate();
103 | if (!L) throw Error("failed to create lua state");
104 |
105 | let luaCode = `
106 | local b
107 | function f(x)
108 | local first = 1
109 | while 1 do
110 | if x == 3 and not first then return end
111 | local a = 'xuxu'
112 | b = function (op, y)
113 | if op == 'set' then
114 | a = x+y
115 | else
116 | return a
117 | end
118 | end
119 | if x == 1 then do break end
120 | elseif x == 2 then return
121 | else if x ~= 3 then error() end
122 | end
123 | first = nil
124 | end
125 | end
126 |
127 | for i=1,3 do
128 | f(i)
129 | assert(b('get') == 'xuxu')
130 | b('set', 10); assert(b('get') == 10+i)
131 | b = nil
132 | end
133 |
134 | pcall(f, 4);
135 | assert(b('get') == 'xuxu')
136 | b('set', 10); assert(b('get') == 14)
137 | `;
138 | lualib.luaL_openlibs(L);
139 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
140 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
141 | lua.lua_call(L, 0, 0);
142 | });
143 |
144 |
145 | test("[test-suite] closure: testing multi-level closure", () => {
146 | let L = lauxlib.luaL_newstate();
147 | if (!L) throw Error("failed to create lua state");
148 |
149 | let luaCode = `
150 | local w
151 | function f(x)
152 | return function (y)
153 | return function (z) return w+x+y+z end
154 | end
155 | end
156 |
157 | y = f(10)
158 | w = 1.345
159 | assert(y(20)(30) == 60+w)
160 | `;
161 | lualib.luaL_openlibs(L);
162 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
163 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
164 | lua.lua_call(L, 0, 0);
165 | });
166 |
167 |
168 | test("[test-suite] closure: testing closures x repeat-until", () => {
169 | let L = lauxlib.luaL_newstate();
170 | if (!L) throw Error("failed to create lua state");
171 |
172 | let luaCode = `
173 | local a = {}
174 | local i = 1
175 | repeat
176 | local x = i
177 | a[i] = function () i = x+1; return x end
178 | until i > 10 or a[i]() ~= x
179 | assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
180 | `;
181 | lualib.luaL_openlibs(L);
182 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
183 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
184 | lua.lua_call(L, 0, 0);
185 | });
186 |
187 |
188 | test("[test-suite] closure: testing closures created in 'then' and 'else' parts of 'if's", () => {
189 | let L = lauxlib.luaL_newstate();
190 | if (!L) throw Error("failed to create lua state");
191 |
192 | let luaCode = `
193 | a = {}
194 | for i = 1, 10 do
195 | if i % 3 == 0 then
196 | local y = 0
197 | a[i] = function (x) local t = y; y = x; return t end
198 | elseif i % 3 == 1 then
199 | goto L1
200 | error'not here'
201 | ::L1::
202 | local y = 1
203 | a[i] = function (x) local t = y; y = x; return t end
204 | elseif i % 3 == 2 then
205 | local t
206 | goto l4
207 | ::l4a:: a[i] = t; goto l4b
208 | error("should never be here!")
209 | ::l4::
210 | local y = 2
211 | t = function (x) local t = y; y = x; return t end
212 | goto l4a
213 | error("should never be here!")
214 | ::l4b::
215 | end
216 | end
217 |
218 | for i = 1, 10 do
219 | assert(a[i](i * 10) == i % 3 and a[i]() == i * 10)
220 | end
221 | `;
222 | lualib.luaL_openlibs(L);
223 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
224 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
225 | lua.lua_call(L, 0, 0);
226 | });
227 |
228 |
229 | test("[test-suite] closure: test for correctly closing upvalues in tail calls of vararg functions", () => {
230 | let L = lauxlib.luaL_newstate();
231 | if (!L) throw Error("failed to create lua state");
232 |
233 | let luaCode = `
234 | local function t ()
235 | local function c(a,b) assert(a=="test" and b=="OK") end
236 | local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
237 | local x = 1
238 | return v(function() return x end)
239 | end
240 | t()
241 | `;
242 | lualib.luaL_openlibs(L);
243 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
244 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
245 | lua.lua_call(L, 0, 0);
246 | });
247 |
--------------------------------------------------------------------------------
/test/test-suite/goto.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../../src/lua.js');
4 | const lauxlib = require('../../src/lauxlib.js');
5 | const lualib = require('../../src/lualib.js');
6 | const {to_luastring} = require("../../src/fengaricore.js");
7 |
8 | test("[test-suite] goto: error messages", () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | local function errmsg (code, m)
14 | local st, msg = load(code)
15 | assert(not st and string.find(msg, m))
16 | end
17 |
18 | -- cannot see label inside block
19 | errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'")
20 | errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'")
21 |
22 | -- repeated label
23 | errmsg([[ ::l1:: ::l1:: ]], "label 'l1'")
24 |
25 |
26 | -- undefined label
27 | errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'")
28 |
29 | -- jumping over variable definition
30 | errmsg([[
31 | do local bb, cc; goto l1; end
32 | local aa
33 | ::l1:: print(3)
34 | ]], "local 'aa'")
35 |
36 | -- jumping into a block
37 | errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'")
38 | errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'")
39 |
40 | -- cannot continue a repeat-until with variables
41 | errmsg([[
42 | repeat
43 | if x then goto cont end
44 | local xuxu = 10
45 | ::cont::
46 | until xuxu < x
47 | ]], "local 'xuxu'")
48 | `;
49 | lualib.luaL_openlibs(L);
50 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
51 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
52 | lua.lua_call(L, 0, 0);
53 | });
54 |
55 |
56 | test("[test-suite] goto", () => {
57 | let L = lauxlib.luaL_newstate();
58 | if (!L) throw Error("failed to create lua state");
59 |
60 | let luaCode = `
61 | -- simple gotos
62 | local x
63 | do
64 | local y = 12
65 | goto l1
66 | ::l2:: x = x + 1; goto l3
67 | ::l1:: x = y; goto l2
68 | end
69 | ::l3:: ::l3_1:: assert(x == 13)
70 |
71 | -- long labels
72 | do
73 | local prog = [[
74 | do
75 | local a = 1
76 | goto l%sa; a = a + 1
77 | ::l%sa:: a = a + 10
78 | goto l%sb; a = a + 2
79 | ::l%sb:: a = a + 20
80 | return a
81 | end
82 | ]]
83 | local label = string.rep("0123456789", 40)
84 | prog = string.format(prog, label, label, label, label)
85 | assert(assert(load(prog))() == 31)
86 | end
87 |
88 | -- goto to correct label when nested
89 | do goto l3; ::l3:: end -- does not loop jumping to previous label 'l3'
90 |
91 | -- ok to jump over local dec. to end of block
92 | do
93 | goto l1
94 | local a = 23
95 | x = a
96 | ::l1::;
97 | end
98 |
99 | while true do
100 | goto l4
101 | goto l1 -- ok to jump over local dec. to end of block
102 | goto l1 -- multiple uses of same label
103 | local x = 45
104 | ::l1:: ;;;
105 | end
106 | ::l4:: assert(x == 13)
107 |
108 | if print then
109 | goto l1 -- ok to jump over local dec. to end of block
110 | error("should not be here")
111 | goto l2 -- ok to jump over local dec. to end of block
112 | local x
113 | ::l1:: ; ::l2:: ;;
114 | else end
115 | `;
116 | lualib.luaL_openlibs(L);
117 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
118 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
119 | lua.lua_call(L, 0, 0);
120 | });
121 |
122 |
123 | test("[test-suite] goto: to repeat a label in a different function is OK", () => {
124 | let L = lauxlib.luaL_newstate();
125 | if (!L) throw Error("failed to create lua state");
126 |
127 | let luaCode = `
128 | local function foo ()
129 | local a = {}
130 | goto l3
131 | ::l1:: a[#a + 1] = 1; goto l2;
132 | ::l2:: a[#a + 1] = 2; goto l5;
133 | ::l3::
134 | ::l3a:: a[#a + 1] = 3; goto l1;
135 | ::l4:: a[#a + 1] = 4; goto l6;
136 | ::l5:: a[#a + 1] = 5; goto l4;
137 | ::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and
138 | a[4] == 5 and a[5] == 4)
139 | if not a[6] then a[6] = true; goto l3a end -- do it twice
140 | end
141 |
142 | ::l6:: foo()
143 | `;
144 | lualib.luaL_openlibs(L);
145 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
146 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
147 | lua.lua_call(L, 0, 0);
148 | });
149 |
150 |
151 | test("[test-suite] goto: bug in 5.2 -> 5.3.2", () => {
152 | let L = lauxlib.luaL_newstate();
153 | if (!L) throw Error("failed to create lua state");
154 |
155 | let luaCode = `
156 | do -- bug in 5.2 -> 5.3.2
157 | local x
158 | ::L1::
159 | local y -- cannot join this SETNIL with previous one
160 | assert(y == nil)
161 | y = true
162 | if x == nil then
163 | x = 1
164 | goto L1
165 | else
166 | x = x + 1
167 | end
168 | assert(x == 2 and y == true)
169 | end
170 | `;
171 | lualib.luaL_openlibs(L);
172 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
173 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
174 | lua.lua_call(L, 0, 0);
175 | });
176 |
177 |
178 | test("[test-suite] goto: testing closing of upvalues", () => {
179 | let L = lauxlib.luaL_newstate();
180 | if (!L) throw Error("failed to create lua state");
181 |
182 | let luaCode = `
183 | local function foo ()
184 | local t = {}
185 | do
186 | local i = 1
187 | local a, b, c, d
188 | t[1] = function () return a, b, c, d end
189 | ::l1::
190 | local b
191 | do
192 | local c
193 | t[#t + 1] = function () return a, b, c, d end -- t[2], t[4], t[6]
194 | if i > 2 then goto l2 end
195 | do
196 | local d
197 | t[#t + 1] = function () return a, b, c, d end -- t[3], t[5]
198 | i = i + 1
199 | local a
200 | goto l1
201 | end
202 | end
203 | end
204 | ::l2:: return t
205 | end
206 |
207 | local a = foo()
208 | assert(#a == 6)
209 |
210 | -- all functions share same 'a'
211 | for i = 2, 6 do
212 | assert(debug.upvalueid(a[1], 1) == debug.upvalueid(a[i], 1))
213 | end
214 |
215 | -- 'b' and 'c' are shared among some of them
216 | for i = 2, 6 do
217 | -- only a[1] uses external 'b'/'b'
218 | assert(debug.upvalueid(a[1], 2) ~= debug.upvalueid(a[i], 2))
219 | assert(debug.upvalueid(a[1], 3) ~= debug.upvalueid(a[i], 3))
220 | end
221 |
222 | for i = 3, 5, 2 do
223 | -- inner functions share 'b'/'c' with previous ones
224 | assert(debug.upvalueid(a[i], 2) == debug.upvalueid(a[i - 1], 2))
225 | assert(debug.upvalueid(a[i], 3) == debug.upvalueid(a[i - 1], 3))
226 | -- but not with next ones
227 | assert(debug.upvalueid(a[i], 2) ~= debug.upvalueid(a[i + 1], 2))
228 | assert(debug.upvalueid(a[i], 3) ~= debug.upvalueid(a[i + 1], 3))
229 | end
230 |
231 | -- only external 'd' is shared
232 | for i = 2, 6, 2 do
233 | assert(debug.upvalueid(a[1], 4) == debug.upvalueid(a[i], 4))
234 | end
235 |
236 | -- internal 'd's are all different
237 | for i = 3, 5, 2 do
238 | for j = 1, 6 do
239 | assert((debug.upvalueid(a[i], 4) == debug.upvalueid(a[j], 4))
240 | == (i == j))
241 | end
242 | end
243 | `;
244 | lualib.luaL_openlibs(L);
245 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
246 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
247 | lua.lua_call(L, 0, 0);
248 | });
249 |
250 |
251 | test("[test-suite] goto: testing if x goto optimizations", () => {
252 | let L = lauxlib.luaL_newstate();
253 | if (!L) throw Error("failed to create lua state");
254 |
255 | let luaCode = `
256 | local function testG (a)
257 | if a == 1 then
258 | goto l1
259 | error("should never be here!")
260 | elseif a == 2 then goto l2
261 | elseif a == 3 then goto l3
262 | elseif a == 4 then
263 | goto l1 -- go to inside the block
264 | error("should never be here!")
265 | ::l1:: a = a + 1 -- must go to 'if' end
266 | else
267 | goto l4
268 | ::l4a:: a = a * 2; goto l4b
269 | error("should never be here!")
270 | ::l4:: goto l4a
271 | error("should never be here!")
272 | ::l4b::
273 | end
274 | do return a end
275 | ::l2:: do return "2" end
276 | ::l3:: do return "3" end
277 | ::l1:: return "1"
278 | end
279 |
280 | assert(testG(1) == "1")
281 | assert(testG(2) == "2")
282 | assert(testG(3) == "3")
283 | assert(testG(4) == 5)
284 | assert(testG(5) == 10)
285 | `;
286 | lualib.luaL_openlibs(L);
287 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
288 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
289 | lua.lua_call(L, 0, 0);
290 | });
291 |
--------------------------------------------------------------------------------
/test/test-suite/locals.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../../src/lua.js');
4 | const lauxlib = require('../../src/lauxlib.js');
5 | const lualib = require('../../src/lualib.js');
6 | const {to_luastring} = require("../../src/fengaricore.js");
7 |
8 | test('[test-suite] locals: bug in 5.1', () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | local function f(x) x = nil; return x end
14 | assert(f(10) == nil)
15 |
16 | local function f() local x; return x end
17 | assert(f(10) == nil)
18 |
19 | local function f(x) x = nil; local y; return x, y end
20 | assert(f(10) == nil and select(2, f(20)) == nil)
21 | `;
22 | lualib.luaL_openlibs(L);
23 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
24 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
25 | lua.lua_call(L, 0, 0);
26 | });
27 |
28 |
29 | test('[test-suite] locals: local scope', () => {
30 | let L = lauxlib.luaL_newstate();
31 | if (!L) throw Error("failed to create lua state");
32 |
33 | let luaCode = `
34 | do
35 | local i = 10
36 | do local i = 100; assert(i==100) end
37 | do local i = 1000; assert(i==1000) end
38 | assert(i == 10)
39 | if i ~= 10 then
40 | local i = 20
41 | else
42 | local i = 30
43 | assert(i == 30)
44 | end
45 | end
46 |
47 | f = nil
48 |
49 | local f
50 | x = 1
51 |
52 | a = nil
53 | load('local a = {}')()
54 | assert(a == nil)
55 |
56 | function f (a)
57 | local _1, _2, _3, _4, _5
58 | local _6, _7, _8, _9, _10
59 | local x = 3
60 | local b = a
61 | local c,d = a,b
62 | if (d == b) then
63 | local x = 'q'
64 | x = b
65 | assert(x == 2)
66 | else
67 | assert(nil)
68 | end
69 | assert(x == 3)
70 | local f = 10
71 | end
72 |
73 | local b=10
74 | local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3
75 |
76 |
77 | assert(x == 1)
78 |
79 | f(2)
80 | assert(type(f) == 'function')
81 | `;
82 | lualib.luaL_openlibs(L);
83 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
84 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
85 | lua.lua_call(L, 0, 0);
86 | });
87 |
88 |
89 | const getenv = `
90 | local function getenv (f)
91 | local a,b = debug.getupvalue(f, 1)
92 | assert(a == '_ENV')
93 | return b
94 | end
95 | `;
96 |
97 |
98 | test('[test-suite] locals: test for global table of loaded chunks', () => {
99 | let L = lauxlib.luaL_newstate();
100 | if (!L) throw Error("failed to create lua state");
101 |
102 | let luaCode = `
103 | assert(getenv(load"a=3") == _G)
104 | local c = {}; local f = load("a = 3", nil, nil, c)
105 | assert(getenv(f) == c)
106 | assert(c.a == nil)
107 | f()
108 | assert(c.a == 3)
109 | `;
110 | lualib.luaL_openlibs(L);
111 | if (lauxlib.luaL_loadstring(L, to_luastring(getenv + luaCode)) === lua.LUA_ERRSYNTAX)
112 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
113 | lua.lua_call(L, 0, 0);
114 | });
115 |
116 |
117 | test('[test-suite] locals: old test for limits for special instructions (now just a generic test)', () => {
118 | let L = lauxlib.luaL_newstate();
119 | if (!L) throw Error("failed to create lua state");
120 |
121 | let luaCode = `
122 | do
123 | local i = 2
124 | local p = 4 -- p == 2^i
125 | repeat
126 | for j=-3,3 do
127 | assert(load(string.format([[local a=%s;
128 | a=a+%s;
129 | assert(a ==2^%s)]], j, p-j, i), '')) ()
130 | assert(load(string.format([[local a=%s;
131 | a=a-%s;
132 | assert(a==-2^%s)]], -j, p-j, i), '')) ()
133 | assert(load(string.format([[local a,b=0,%s;
134 | a=b-%s;
135 | assert(a==-2^%s)]], -j, p-j, i), '')) ()
136 | end
137 | p = 2 * p; i = i + 1
138 | until p <= 0
139 | end
140 | `;
141 | lualib.luaL_openlibs(L);
142 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
143 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
144 | lua.lua_call(L, 0, 0);
145 | });
146 |
147 |
148 | test('[test-suite] locals: testing lexical environments', () => {
149 | let L = lauxlib.luaL_newstate();
150 | if (!L) throw Error("failed to create lua state");
151 |
152 | let luaCode = `
153 | assert(_ENV == _G)
154 |
155 | do
156 | local dummy
157 | local _ENV = (function (...) return ... end)(_G, dummy) -- {
158 |
159 | do local _ENV = {assert=assert}; assert(true) end
160 | mt = {_G = _G}
161 | local foo,x
162 | A = false -- "declare" A
163 | do local _ENV = mt
164 | function foo (x)
165 | A = x
166 | do local _ENV = _G; A = 1000 end
167 | return function (x) return A .. x end
168 | end
169 | end
170 | assert(getenv(foo) == mt)
171 | x = foo('hi'); assert(mt.A == 'hi' and A == 1000)
172 | assert(x('*') == mt.A .. '*')
173 |
174 | do local _ENV = {assert=assert, A=10};
175 | do local _ENV = {assert=assert, A=20};
176 | assert(A==20);x=A
177 | end
178 | assert(A==10 and x==20)
179 | end
180 | assert(x==20)
181 | end
182 | `;
183 | lualib.luaL_openlibs(L);
184 | if (lauxlib.luaL_loadstring(L, to_luastring(getenv + luaCode)) === lua.LUA_ERRSYNTAX)
185 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
186 | lua.lua_call(L, 0, 0);
187 | });
188 |
--------------------------------------------------------------------------------
/test/test-suite/pm-classes.lua:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengari-lua/fengari/bd945f0ef42caad86aff59365fcb8a3ec8509d06/test/test-suite/pm-classes.lua
--------------------------------------------------------------------------------
/test/test-suite/pm-gsub.lua:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengari-lua/fengari/bd945f0ef42caad86aff59365fcb8a3ec8509d06/test/test-suite/pm-gsub.lua
--------------------------------------------------------------------------------
/test/test-suite/utf8.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../../src/lua.js');
4 | const lauxlib = require('../../src/lauxlib.js');
5 | const lualib = require('../../src/lualib.js');
6 | const {to_luastring} = require("../../src/fengaricore.js");
7 |
8 | const prefix = `
9 | local function checkerror (msg, f, ...)
10 | local s, err = pcall(f, ...)
11 | assert(not s and string.find(err, msg))
12 | end
13 |
14 |
15 | local function len (s)
16 | return #string.gsub(s, "[\\x80-\\xBF]", "")
17 | end
18 |
19 |
20 | local justone = "^" .. utf8.charpattern .. "$"
21 |
22 | local function checksyntax (s, t)
23 | local ts = {"return '"}
24 | for i = 1, #t do ts[i + 1] = string.format("\\\\u{%x}", t[i]) end
25 | ts[#t + 2] = "'"
26 | ts = table.concat(ts)
27 | assert(assert(load(ts))() == s)
28 | end
29 | -- 't' is the list of codepoints of 's'
30 | local function check (s, t)
31 | local l = utf8.len(s)
32 | assert(#t == l and len(s) == l)
33 | assert(utf8.char(table.unpack(t)) == s)
34 |
35 | assert(utf8.offset(s, 0) == 1)
36 |
37 | checksyntax(s, t)
38 |
39 | local t1 = {utf8.codepoint(s, 1, -1)}
40 | assert(#t == #t1)
41 | for i = 1, #t do assert(t[i] == t1[i]) end
42 |
43 | for i = 1, l do
44 | local pi = utf8.offset(s, i) -- position of i-th char
45 | local pi1 = utf8.offset(s, 2, pi) -- position of next char
46 | assert(string.find(string.sub(s, pi, pi1 - 1), justone))
47 | assert(utf8.offset(s, -1, pi1) == pi)
48 | assert(utf8.offset(s, i - l - 1) == pi)
49 | assert(pi1 - pi == #utf8.char(utf8.codepoint(s, pi)))
50 | for j = pi, pi1 - 1 do
51 | assert(utf8.offset(s, 0, j) == pi)
52 | end
53 | for j = pi + 1, pi1 - 1 do
54 | assert(not utf8.len(s, j))
55 | end
56 | assert(utf8.len(s, pi, pi) == 1)
57 | assert(utf8.len(s, pi, pi1 - 1) == 1)
58 | assert(utf8.len(s, pi) == l - i + 1)
59 | assert(utf8.len(s, pi1) == l - i)
60 | assert(utf8.len(s, 1, pi) == i)
61 | end
62 |
63 | local i = 0
64 | for p, c in utf8.codes(s) do
65 | i = i + 1
66 | assert(c == t[i] and p == utf8.offset(s, i))
67 | assert(utf8.codepoint(s, p) == c)
68 | end
69 | assert(i == #t)
70 |
71 | i = 0
72 | for p, c in utf8.codes(s) do
73 | i = i + 1
74 | assert(c == t[i] and p == utf8.offset(s, i))
75 | end
76 | assert(i == #t)
77 |
78 | i = 0
79 | for c in string.gmatch(s, utf8.charpattern) do
80 | i = i + 1
81 | assert(c == utf8.char(t[i]))
82 | end
83 | assert(i == #t)
84 |
85 | for i = 1, l do
86 | assert(utf8.offset(s, i) == utf8.offset(s, i - l - 1, #s + 1))
87 | end
88 |
89 | end
90 |
91 | local function invalid (s)
92 | checkerror("invalid UTF%-8 code", utf8.codepoint, s)
93 | assert(not utf8.len(s))
94 | end
95 | `;
96 |
97 | test("[test-suite] utf8: offset", () => {
98 | let L = lauxlib.luaL_newstate();
99 | if (!L) throw Error("failed to create lua state");
100 |
101 | let luaCode = prefix + `
102 | assert(utf8.offset("alo", 5) == nil)
103 | assert(utf8.offset("alo", -4) == nil)
104 | `;
105 | lualib.luaL_openlibs(L);
106 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
107 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
108 | lua.lua_call(L, 0, 0);
109 | });
110 |
111 |
112 | test("[test-suite] utf8: error indication in utf8.len", () => {
113 | let L = lauxlib.luaL_newstate();
114 | if (!L) throw Error("failed to create lua state");
115 |
116 | let luaCode = prefix + `
117 | do
118 | local function check (s, p)
119 | local a, b = utf8.len(s)
120 | assert(not a and b == p)
121 | end
122 | check("abc\\xE3def", 4)
123 | check("汉字\\x80", #("汉字") + 1)
124 | check("\\xF4\\x9F\\xBF", 1)
125 | check("\\xF4\\x9F\\xBF\\xBF", 1)
126 | end
127 | `;
128 | lualib.luaL_openlibs(L);
129 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
130 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
131 | lua.lua_call(L, 0, 0);
132 | });
133 |
134 |
135 | test("[test-suite] utf8: error in initial position for offset", () => {
136 | let L = lauxlib.luaL_newstate();
137 | if (!L) throw Error("failed to create lua state");
138 |
139 | let luaCode = prefix + `
140 | checkerror("position out of range", utf8.offset, "abc", 1, 5)
141 | checkerror("position out of range", utf8.offset, "abc", 1, -4)
142 | checkerror("position out of range", utf8.offset, "", 1, 2)
143 | checkerror("position out of range", utf8.offset, "", 1, -1)
144 | checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
145 | checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
146 | checkerror("continuation byte", utf8.offset, "\\x80", 1)
147 | `;
148 | lualib.luaL_openlibs(L);
149 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
150 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
151 | lua.lua_call(L, 0, 0);
152 | });
153 |
154 |
155 | test("[test-suite] utf8: codepoints", () => {
156 | let L = lauxlib.luaL_newstate();
157 | if (!L) throw Error("failed to create lua state");
158 |
159 | let luaCode = prefix + `
160 | local s = "hello World"
161 | local t = {string.byte(s, 1, -1)}
162 | for i = 1, utf8.len(s) do assert(t[i] == string.byte(s, i)) end
163 | check(s, t)
164 |
165 | check("汉字/漢字", {27721, 23383, 47, 28450, 23383,})
166 |
167 | do
168 | local s = "áéí\\128"
169 | local t = {utf8.codepoint(s,1,#s - 1)}
170 | assert(#t == 3 and t[1] == 225 and t[2] == 233 and t[3] == 237)
171 | checkerror("invalid UTF%-8 code", utf8.codepoint, s, 1, #s)
172 | checkerror("out of range", utf8.codepoint, s, #s + 1)
173 | t = {utf8.codepoint(s, 4, 3)}
174 | assert(#t == 0)
175 | checkerror("out of range", utf8.codepoint, s, -(#s + 1), 1)
176 | checkerror("out of range", utf8.codepoint, s, 1, #s + 1)
177 | end
178 |
179 | assert(utf8.char() == "")
180 | assert(utf8.char(97, 98, 99) == "abc")
181 |
182 | assert(utf8.codepoint(utf8.char(0x10FFFF)) == 0x10FFFF)
183 |
184 | checkerror("value out of range", utf8.char, 0x10FFFF + 1)
185 | `;
186 | lualib.luaL_openlibs(L);
187 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
188 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
189 | lua.lua_call(L, 0, 0);
190 | });
191 |
192 |
193 | test("[test-suite] utf8: UTF-8 representation for 0x11ffff (value out of valid range)", () => {
194 | let L = lauxlib.luaL_newstate();
195 | if (!L) throw Error("failed to create lua state");
196 |
197 | let luaCode = prefix + `
198 | invalid("\\xF4\\x9F\\xBF\\xBF")
199 | `;
200 | lualib.luaL_openlibs(L);
201 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
202 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
203 | lua.lua_call(L, 0, 0);
204 | });
205 |
206 |
207 | test("[test-suite] utf8: overlong sequences", () => {
208 | let L = lauxlib.luaL_newstate();
209 | if (!L) throw Error("failed to create lua state");
210 |
211 | let luaCode = prefix + `
212 | invalid("\\xC0\\x80") -- zero
213 | invalid("\\xC1\\xBF") -- 0x7F (should be coded in 1 byte)
214 | invalid("\\xE0\\x9F\\xBF") -- 0x7FF (should be coded in 2 bytes)
215 | invalid("\\xF0\\x8F\\xBF\\xBF") -- 0xFFFF (should be coded in 3 bytes)
216 | `;
217 | lualib.luaL_openlibs(L);
218 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
219 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
220 | lua.lua_call(L, 0, 0);
221 | });
222 |
223 |
224 | test("[test-suite] utf8: invalid bytes", () => {
225 | let L = lauxlib.luaL_newstate();
226 | if (!L) throw Error("failed to create lua state");
227 |
228 | let luaCode = prefix + `
229 | invalid("\\x80") -- continuation byte
230 | invalid("\\xBF") -- continuation byte
231 | invalid("\\xFE") -- invalid byte
232 | invalid("\\xFF") -- invalid byte
233 | `;
234 | lualib.luaL_openlibs(L);
235 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
236 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
237 | lua.lua_call(L, 0, 0);
238 | });
239 |
240 |
241 | test("[test-suite] utf8: empty strings", () => {
242 | let L = lauxlib.luaL_newstate();
243 | if (!L) throw Error("failed to create lua state");
244 |
245 | let luaCode = prefix + `
246 | check("", {})
247 | `;
248 | lualib.luaL_openlibs(L);
249 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
250 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
251 | lua.lua_call(L, 0, 0);
252 | });
253 |
254 |
255 | test("[test-suite] utf8: minimum and maximum values for each sequence size", () => {
256 | let L = lauxlib.luaL_newstate();
257 | if (!L) throw Error("failed to create lua state");
258 |
259 | let luaCode = prefix + `
260 | s = "\\0 \\x7F\\z
261 | \\xC2\\x80 \\xDF\\xBF\\z
262 | \\xE0\\xA0\\x80 \\xEF\\xBF\\xBF\\z
263 | \\xF0\\x90\\x80\\x80 \\xF4\\x8F\\xBF\\xBF"
264 | s = string.gsub(s, " ", "")
265 | check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF})
266 |
267 | x = "日本語a-4\\0éó"
268 | check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
269 |
270 | -- Supplementary Characters
271 | check("𣲷𠜎𠱓𡁻𠵼ab𠺢",
272 | {0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,})
273 |
274 | check("𨳊𩶘𦧺𨳒𥄫𤓓\\xF4\\x8F\\xBF\\xBF",
275 | {0x28CCA, 0x29D98, 0x269FA, 0x28CD2, 0x2512B, 0x244D3, 0x10ffff})
276 |
277 |
278 | local i = 0
279 | for p, c in string.gmatch(x, "()(" .. utf8.charpattern .. ")") do
280 | i = i + 1
281 | assert(utf8.offset(x, i) == p)
282 | assert(utf8.len(x, p) == utf8.len(x) - i + 1)
283 | assert(utf8.len(c) == 1)
284 | for j = 1, #c - 1 do
285 | assert(utf8.offset(x, 0, p + j - 1) == p)
286 | end
287 | end
288 | `;
289 | lualib.luaL_openlibs(L);
290 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
291 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
292 | lua.lua_call(L, 0, 0);
293 | });
294 |
--------------------------------------------------------------------------------
/test/test-suite/vararg.test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require('../../src/lua.js');
4 | const lauxlib = require('../../src/lauxlib.js');
5 | const lualib = require('../../src/lualib.js');
6 | const {to_luastring} = require("../../src/fengaricore.js");
7 |
8 | test("[test-suite] vararg: testing vararg", () => {
9 | let L = lauxlib.luaL_newstate();
10 | if (!L) throw Error("failed to create lua state");
11 |
12 | let luaCode = `
13 | function f(a, ...)
14 | local arg = {n = select('#', ...), ...}
15 | for i=1,arg.n do assert(a[i]==arg[i]) end
16 | return arg.n
17 | end
18 |
19 | function c12 (...)
20 | assert(arg == _G.arg) -- no local 'arg'
21 | local x = {...}; x.n = #x
22 | local res = (x.n==2 and x[1] == 1 and x[2] == 2)
23 | if res then res = 55 end
24 | return res, 2
25 | end
26 |
27 | function vararg (...) return {n = select('#', ...), ...} end
28 |
29 | local call = function (f, args) return f(table.unpack(args, 1, args.n)) end
30 |
31 | assert(f() == 0)
32 | assert(f({1,2,3}, 1, 2, 3) == 3)
33 | assert(f({"alo", nil, 45, f, nil}, "alo", nil, 45, f, nil) == 5)
34 |
35 | assert(c12(1,2)==55)
36 | a,b = assert(call(c12, {1,2}))
37 | assert(a == 55 and b == 2)
38 | a = call(c12, {1,2;n=2})
39 | assert(a == 55 and b == 2)
40 | a = call(c12, {1,2;n=1})
41 | assert(not a)
42 | assert(c12(1,2,3) == false)
43 | local a = vararg(call(next, {_G,nil;n=2}))
44 | local b,c = next(_G)
45 | assert(a[1] == b and a[2] == c and a.n == 2)
46 | a = vararg(call(call, {c12, {1,2}}))
47 | assert(a.n == 2 and a[1] == 55 and a[2] == 2)
48 | a = call(function()end, {'+'})
49 | assert(a == nil)
50 |
51 | local t = {1, 10}
52 | function t:f (...) local arg = {...}; return self[...]+#arg end
53 | assert(t:f(1,4) == 3 and t:f(2) == 11)
54 |
55 | lim = 20
56 | local i, a = 1, {}
57 | while i <= lim do a[i] = i+0.3; i=i+1 end
58 |
59 | function f(a, b, c, d, ...)
60 | local more = {...}
61 | assert(a == 1.3 and more[1] == 5.3 and
62 | more[lim-4] == lim+0.3 and not more[lim-3])
63 | end
64 |
65 | function g(a,b,c)
66 | assert(a == 1.3 and b == 2.3 and c == 3.3)
67 | end
68 |
69 | call(f, a)
70 | call(g, a)
71 |
72 | a = {}
73 | i = 1
74 | while i <= lim do a[i] = i; i=i+1 end
75 | assert(call(math.max, a) == lim)
76 | `;
77 | lualib.luaL_openlibs(L);
78 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
79 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
80 | lua.lua_call(L, 0, 0);
81 | });
82 |
83 |
84 | test("[test-suite] vararg: new-style varargs", () => {
85 | let L = lauxlib.luaL_newstate();
86 | if (!L) throw Error("failed to create lua state");
87 |
88 | let luaCode = `
89 | function oneless (a, ...) return ... end
90 |
91 | function f (n, a, ...)
92 | local b
93 | assert(arg == _G.arg) -- no local 'arg'
94 | if n == 0 then
95 | local b, c, d = ...
96 | return a, b, c, d, oneless(oneless(oneless(...)))
97 | else
98 | n, b, a = n-1, ..., a
99 | assert(b == ...)
100 | return f(n, a, ...)
101 | end
102 | end
103 |
104 | a,b,c,d,e = assert(f(10,5,4,3,2,1))
105 | assert(a==5 and b==4 and c==3 and d==2 and e==1)
106 |
107 | a,b,c,d,e = f(4)
108 | assert(a==nil and b==nil and c==nil and d==nil and e==nil)
109 | `;
110 | lualib.luaL_openlibs(L);
111 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
112 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
113 | lua.lua_call(L, 0, 0);
114 | });
115 |
116 |
117 | test("[test-suite] vararg: varargs for main chunks", () => {
118 | let L = lauxlib.luaL_newstate();
119 | if (!L) throw Error("failed to create lua state");
120 |
121 | let luaCode = `
122 | f = load[[ return {...} ]]
123 | x = f(2,3)
124 | assert(x[1] == 2 and x[2] == 3 and x[3] == nil)
125 |
126 |
127 | f = load[[
128 | local x = {...}
129 | for i=1,select('#', ...) do assert(x[i] == select(i, ...)) end
130 | assert(x[select('#', ...)+1] == nil)
131 | return true
132 | ]]
133 |
134 | assert(f("a", "b", nil, {}, assert))
135 | assert(f())
136 |
137 | a = {select(3, table.unpack{10,20,30,40})}
138 | assert(#a == 2 and a[1] == 30 and a[2] == 40)
139 | a = {select(1)}
140 | assert(next(a) == nil)
141 | a = {select(-1, 3, 5, 7)}
142 | assert(a[1] == 7 and a[2] == nil)
143 | a = {select(-2, 3, 5, 7)}
144 | assert(a[1] == 5 and a[2] == 7 and a[3] == nil)
145 | pcall(select, 10000)
146 | pcall(select, -10000)
147 | `;
148 | lualib.luaL_openlibs(L);
149 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
150 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
151 | lua.lua_call(L, 0, 0);
152 | });
153 |
154 |
155 | test("[test-suite] vararg: bug in 5.2.2", () => {
156 | let L = lauxlib.luaL_newstate();
157 | if (!L) throw Error("failed to create lua state");
158 |
159 | let luaCode = `
160 | function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
161 | p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
162 | p21, p22, p23, p24, p25, p26, p27, p28, p29, p30,
163 | p31, p32, p33, p34, p35, p36, p37, p38, p39, p40,
164 | p41, p42, p43, p44, p45, p46, p48, p49, p50, ...)
165 | local a1,a2,a3,a4,a5,a6,a7
166 | local a8,a9,a10,a11,a12,a13,a14
167 | end
168 |
169 | -- assertion fail here
170 | f()
171 | `;
172 | lualib.luaL_openlibs(L);
173 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) === lua.LUA_ERRSYNTAX)
174 | throw new SyntaxError(lua.lua_tojsstring(L, -1));
175 | lua.lua_call(L, 0, 0);
176 | });
177 |
--------------------------------------------------------------------------------
/test/tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const lua = require("../src/lua.js");
4 | const lauxlib = require("../src/lauxlib.js");
5 | const {to_luastring} = require("../src/fengaricore.js");
6 |
7 | const toByteCode = function(luaCode) {
8 | let L = lauxlib.luaL_newstate();
9 | if (!L) throw Error("failed to create lua state");
10 |
11 | if (lauxlib.luaL_loadstring(L, to_luastring(luaCode)) !== lua.LUA_OK)
12 | throw Error(lua.lua_tojsstring(L, -1));
13 |
14 | let b = [];
15 | if (lua.lua_dump(L, function(L, b, size, B) {
16 | B.push(...b.slice(0, size));
17 | return 0;
18 | }, b, false) !== 0)
19 | throw Error("unable to dump given function");
20 | return Uint8Array.from(b);
21 | };
22 |
23 | module.exports.toByteCode = toByteCode;
24 |
--------------------------------------------------------------------------------