├── .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 | [![Build Status](https://github.com/fengari-lua/fengari/actions/workflows/ci.yaml/badge.svg)](https://github.com/fengari-lua/fengari/actions/workflows/ci.yaml?query=event%3Apush) 2 | [![npm](https://img.shields.io/npm/v/fengari.svg)](https://npmjs.com/package/fengari) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | [![#fengari on libera.chat](https://img.shields.io/badge/chat-%23fengari-brightgreen)](https://web.libera.chat/?channels=#fengari) 5 | [![Code Quality: Javascript](https://img.shields.io/lgtm/grade/javascript/g/fengari-lua/fengari.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/fengari-lua/fengari/context:javascript) 6 | [![Total Alerts](https://img.shields.io/lgtm/alerts/g/fengari-lua/fengari.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/fengari-lua/fengari/alerts) 7 | 8 |

9 | Fengari 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 | --------------------------------------------------------------------------------