Compare commits

...

894 Commits

Author SHA1 Message Date
Alex Orlenko 4ff677f5fc v0.10.4 2025-05-05 15:43:00 +01:00
Alex Orlenko 5b920321dd mlua-sys: v0.7.0 2025-05-05 13:25:55 +01:00
Alex Orlenko 6bb99783e9 Update CHANGELOG 2025-05-05 13:16:26 +01:00
Alex Orlenko 547f82acc1 Bump TARGET_MLUA_LUAU_ABI_VERSION 2025-05-05 12:13:50 +01:00
Alex Orlenko 9ea98e3f1e Sync mlua-sys with the main branch 2025-05-05 12:02:13 +01:00
Alex Orlenko 0ad03bee98 Update README 2025-05-05 11:59:42 +01:00
Alex Orlenko ba7c331984 Update __mlua_async_poll chunk name 2025-05-05 01:02:15 +01:00
Alex Orlenko 53dea3166a Update Lua* dependencies 2025-05-05 01:01:57 +01:00
Alex Orlenko 129dec43ed Silence clippy false positives 2025-05-05 00:59:06 +01:00
Alex Orlenko a7f58e57ec Update scoped userdata mismatch tests 2025-05-05 00:59:00 +01:00
Alex Orlenko e6871525c4 Check that type passed to scoped userdata self argument is userdata.
If passed type is non-userdata we try to get a pointer (which will be null) that triggers an assertion.
Having a check also allow us to generate right error message.
Fixes #569
2025-05-05 00:58:46 +01:00
Alex Orlenko c4956dbf49 Make StateGuard automatically enabled inside callback_error_ext.
Remove manual usage of `StateGuard` in other places.
Closes #567
2025-05-05 00:57:23 +01:00
Alex Orlenko d0ea428e23 Add encode_empty_tables_as_array serialize option.
This will change the behaviour of encoding empty Lua tables into array instead of map.
2025-05-05 00:52:22 +01:00
Alex Orlenko 3e87d3ea9b Optimize Table::is_empty 2025-05-05 00:52:14 +01:00
krakow10 0f8bde4b4e Fix Reversed Comments & Typo (#560)
* Fix reversed comments

* Fix typos
2025-05-05 00:52:07 +01:00
Alex Orlenko b9c9dfbd03 mlua-sys: Update Luau to 0.671 2025-05-05 00:51:38 +01:00
Alex Orlenko 71824297f6 mlua-sys: Add definitions for Luau require library (since 0.669) 2025-05-05 00:50:39 +01:00
Alex Orlenko c0a8064228 Update references to mlua repo 2025-05-05 00:50:31 +01:00
Alex Orlenko 84efb8bf95 Fix warnings when testing documentation 2025-05-05 00:50:19 +01:00
Alex Orlenko cf687a6ac6 Make Lua::weak() method and WeakLua struct public.
This can be useful to prevent circular dependencies between Rust and Lua
or check that Lua instance is still alive.
2025-05-05 00:50:12 +01:00
Alex Orlenko ea85edb242 Update dependencies 2025-05-05 00:49:41 +01:00
Alex Orlenko 788175e0d6 Restrict access to Luau VM from UserData destructors.
It's unsafe to make almost any Lua calls when userdata destructor is running.
This can cause recursive GC run and crash.
See https://github.com/luau-lang/luau/pull/510 for some details.
2025-05-05 00:49:26 +01:00
Alex Orlenko 90ef25a6ee Prepare for custom (Luau) userdata destructors.
We need to add logic later to prevent calling any Lua functions when UserData destructor is running.
2025-05-05 00:49:17 +01:00
Alex Orlenko ac72f6acae Do not propagate collect_garbage flag when clonning Lua 2025-05-05 00:47:39 +01:00
Alex Orlenko 4444ac4ea3 Add AnyUserData::type_id method 2025-05-05 00:47:09 +01:00
Alex Orlenko 5f950e30ec Update Lua::poll_pending doc (still hidden) 2025-05-05 00:47:02 +01:00
Alex Orlenko 311aa5f6e1 Bump Luau to 0.667 2025-05-05 00:46:45 +01:00
Alex Orlenko 739d5f5262 Reduce stack operations when creating userdata 2025-05-05 00:46:28 +01:00
Alex Orlenko 375028e13f Add Chunk::name(), Chunk::environment() and Chunk::mode() functions.
They can be used to retrieve existing chunk params.
2025-05-05 00:46:20 +01:00
Alex Orlenko 46e949c184 Use c string literal where appropriate 2025-05-05 00:44:05 +01:00
Alex Orlenko 62b53e218c Move some userdata helpers from crate::util to crate::userdata::util 2025-05-05 00:43:12 +01:00
Alex Orlenko 0393406b9f Refactor userdata-wrappers feature.
Support borrowing underlying data in `UserDataRef` and `UserDataRefMut`.
2025-05-04 18:45:42 +01:00
Alex Orlenko 272cfdb89b Change Lua library name constants from &str to *const c_char.
It makes easier to pass them to Lua API functions.
2025-05-04 12:49:08 +01:00
Alex Orlenko 8e244a25ea Update test case 2025-05-04 12:48:47 +01:00
Alex Orlenko ef8b8e11ec Support 52-bit integers for Luau
Simply to float conversion (it actually never fails or goes out of range)
2025-05-04 12:48:34 +01:00
Alex Orlenko 2543414726 Fix clippy warnings 2025-05-04 12:47:30 +01:00
Alex Orlenko ee112568d6 Remove Roblox from references to Luau
Closes #537
2025-05-04 12:46:47 +01:00
Alex Orlenko 69d3ddec29 Remove (internal) borrow counter and use instead "locked" flag and strong reference counter 2025-05-04 12:46:35 +01:00
Alex Orlenko 5b0d811c5a Add Variadic to prelude 2025-05-04 12:31:13 +01:00
Alex Orlenko 19536db976 Add bstr/serde dependency if serialize feature flag is enabled 2025-05-04 12:30:52 +01:00
Alex Orlenko fc69551f87 Fix tests 2025-05-04 12:29:57 +01:00
Alex Orlenko a68b0b6905 Do not allow recursive warnings (Lua 5.4) 2025-05-04 12:29:51 +01:00
Alex Orlenko 1ec1cc9922 Fix tests 2025-05-04 12:28:40 +01:00
Joel Natividad fd68b033f0 Fix typos (#522)
* fix various typos in the codebase

* fix typos in CHANGELOG.md
2025-05-04 12:28:24 +01:00
Andrew Farkas c3817409a8 Fix version number in changelog (#521) 2025-05-04 12:28:04 +01:00
Alex Orlenko 863d8092d6 Imporove BorrowedStr/BorrowedBytes ergonomic.
Implement `FromLua` and `IntoLua` for these types to allow working with them directly.
2025-05-04 12:27:24 +01:00
Alex Orlenko 24b6ff3c20 Improve From/Into Lua char conversion 2025-05-04 12:18:28 +01:00
tk bc36261f5c impl FromLua/IntoLua for char (#516) 2025-05-04 12:18:22 +01:00
Alex Orlenko bcb9a4d220 Enable Thread::reset() for all Lua versions 2025-05-04 12:07:55 +01:00
Alex Orlenko 9caf3542d9 v0.10.3 2025-01-27 21:33:51 +00:00
Alex Orlenko cb45db05fa Update README/CHANGELOG 2025-01-27 21:33:24 +00:00
Alex Orlenko d1cb2a9a96 mlua-sys: v0.6.7 2025-01-27 21:20:47 +00:00
Alex Orlenko aa3f6ba46c Fix prototype of new Luau compiler options and methods 2025-01-27 21:19:31 +00:00
Alex Orlenko cc57bed4c8 Update Luau to 0.657 2025-01-27 14:19:40 +00:00
Radiant b5d38ab2e3 Set Default for LuaValue to be nil. (#512) 2025-01-19 23:52:58 +00:00
Alex Orlenko cd4091f64d Allow exhaustive match on Value.
It was not possible because `ValueRef` variant in `Value::Other` was private.
Closes #502 and #503
2024-12-10 23:37:19 +00:00
Alex Orlenko 91e069a77e Optimize (and simplify) protected mode for Rust function calls 2024-12-07 00:04:12 +00:00
Alex Orlenko cacd3dc70f Add Table::set_safeenv method (Luau) 2024-12-04 10:40:49 +00:00
Alex Orlenko 6f6cda0099 v0.10.2 2024-12-01 13:04:06 +00:00
Alex Orlenko d51ce86142 mlua-sys: v0.6.6 2024-12-01 12:56:39 +00:00
Alex Orlenko aa061bce6f mlua_derive: v0.10.1 2024-12-01 12:55:50 +00:00
Alex Orlenko 1c6b6ad801 Fix tests 2024-12-01 12:54:35 +00:00
Evie 031854fa2a Switch proc-macro-error to proc-macro-error2 (#493) 2024-11-30 22:37:40 +00:00
Alex Orlenko 5fd96c7908 Don't run GC finalizers on ref thread.
If this happen then we don't have full access to the ref thread stack.
Fixes #491
2024-11-30 01:00:31 +00:00
Alex Orlenko 7a3f19b857 Ensure that buffer with Luau compiled code is always freed 2024-11-29 13:36:51 +00:00
Alex Orlenko 55a5d7ef10 Protect Lua(u) during chunk loading if memory limit is enforced
Relates to #488
2024-11-27 00:34:21 +00:00
Alex Orlenko af31dbd180 Use c string literal in few places 2024-11-25 22:53:59 +00:00
Alex Orlenko 9ae3cb0a7c Add doc about possible chunk name prefixes 2024-11-24 12:00:06 +00:00
Alex Orlenko fc1c80c142 Mark Chunk::wrap as non-public 2024-11-22 20:03:17 +00:00
Alex Orlenko ee7ced6334 Add Chunk::wrap method 2024-11-22 14:40:52 +00:00
Alex Orlenko d8307d0e4c Reduce visibility of Wrapped* structs 2024-11-22 13:08:57 +00:00
Alex Orlenko bf9fcc5aca Simplify WrappedString 2024-11-22 13:06:50 +00:00
Alex Orlenko 7ce6b97da9 Add String::wrap method to wrap arbitrary AsRef<[u8]> 2024-11-22 11:48:20 +00:00
Alex Orlenko 4891a6ac10 Fix utf-8 test when bstr v1.11 switchted to LowerHex 2024-11-17 18:01:05 +00:00
Alex Orlenko 4ef0d583fc impl Send for UserDataTypeId (in send mode) 2024-11-16 01:56:18 +00:00
cos 30b0122f5d Make build script work with OpenBSD
On OpenBSD the pkg-config names takes a minimalist format. No dash, and
no separator between the major and minor version number. Build script is
thus adapted to have also these in their set of alt_probes.

Signed-off-by: Alex Orlenko <zxteam@protonmail.com>
2024-11-16 01:52:43 +00:00
cos c31c72076f Make build script work with FreeBSD
On FreeBSD the pkg-config names takes the format with a dash, rather
than without (e.g. lua-5.4, not lua5.4). Thus adapt build script to
iterate over an array of alt_probes until finding a match.

Signed-off-by: Alex Orlenko <zxteam@protonmail.com>
2024-11-16 01:49:02 +00:00
Alex Orlenko 3bfaee4ecc Delay "any" userdata metatable creation until first instance 2024-11-16 01:47:04 +00:00
Alex Orlenko 89b68e2a24 Remove generic from RawLua::push_userdata_metatable.
This should help to reduce amount of generated code.
2024-11-16 01:44:17 +00:00
Alex Orlenko cbf805f492 Avoid ptr->usize->ptr conversion to comply strict provenance 2024-11-15 21:47:19 +00:00
Alex Orlenko c926327a6a v0.10.1 2024-11-09 20:06:04 +00:00
Alex Orlenko 7c099500d0 mlua-sys: v0.6.5 2024-11-09 14:30:33 +00:00
Alex Orlenko 8c889cc353 Add String::display method 2024-11-09 14:24:43 +00:00
Alex Orlenko 958abd050e Update String::to_string_lossy doc 2024-11-09 14:10:45 +00:00
Alex Orlenko 92a8203e1c Fix formatting 2024-11-09 13:58:06 +00:00
Alex Orlenko b34b90eca3 Fix wrong formatting table with string keys that are numbers 2024-11-09 13:51:55 +00:00
Alex Orlenko 7aad0adcb4 Update links to luau.org 2024-11-09 12:48:15 +00:00
Alex Orlenko a3cd25db7a Support Luau 0.650 native vector library 2024-11-09 12:44:00 +00:00
Alex Orlenko a4bfeb7752 clippy 2024-11-09 12:38:05 +00:00
vhyrro 0fda512938 feat(table): improve pretty-printing for simple tables and lists (#478) 2024-11-09 12:08:49 +00:00
Alex Orlenko 58e0661086 Merge Scope::attach_destructor into Scope::seal_userdata 2024-11-08 15:04:46 +00:00
Alex Orlenko c7094d470f Add Scope::create_any_userdata to create Lua objects from any non-static Rust types. 2024-11-07 19:44:18 +00:00
Alex Orlenko a7d0691e10 Add AnyUserData::destroy method 2024-11-07 16:12:20 +00:00
Alex Orlenko 05778fbe6f Don't store and use wrong main Lua state in module mode (Lua 5.1/JIT only).
When mlua module is loaded from a non-main coroutine we store a reference to it to use later.
If the coroutine is destroyed by GC we can pass a wrong pointer to Lua that will trigger a segfault.
Instead, set main_state as Option and use current (active) state if needed.
Relates to #479
2024-11-04 15:48:22 +00:00
Alex Orlenko b34d67ec41 Update Luau to 0.650 (luau0-src 0.11.1) 2024-11-03 14:49:12 +00:00
Alex Orlenko 46ee7ea772 Update tarpaulin.toml 2024-11-03 12:54:21 +00:00
Alex Orlenko 15738dda1f Update tarpaulin.toml to include userdata-wrappers 2024-11-03 12:19:22 +00:00
Alex Orlenko 1f32754f05 Relax UserDataBorrowRef restrictions to allow recursive calls 2024-11-03 12:18:00 +00:00
Alex Orlenko c2eab173c5 Add userdata-wrappers feature
This feature allow to opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
Close #470
2024-11-03 11:48:47 +00:00
Alex Orlenko bb311349ec Switch between shared and exclusive lock for UserDataRef depending if T: Sync or not. 2024-11-02 10:38:25 +00:00
Alex Orlenko 928e1d9221 Revert &Scope to &mut Scope 2024-10-31 18:41:47 +00:00
Alex Orlenko 5b8681dcf2 Add Scope::add_destructor to attach custom destructors 2024-10-31 14:35:21 +00:00
Alex Orlenko 4e9a17707b Fix tests 2024-10-31 09:23:03 +00:00
Alex Orlenko 6066089cc1 Skip setting Send/Sync in non-send mode for UserDataCell 2024-10-30 23:32:13 +00:00
Alex Orlenko a8d5f23818 Add Lua::try_app_data_ref and Lua::try_app_data_mut 2024-10-30 15:22:55 +00:00
Alex Orlenko 5ec4e0338a Add From<Vec> and Into<Vec> support to MultiValue and Variadic types 2024-10-30 13:03:08 +00:00
Alex Orlenko d27d1365b5 Update v0.10 release notes (add breaking changes) 2024-10-30 00:42:31 +00:00
Alex Orlenko 76b896edcc Fix attaching __gc metamethod
Bug introdused in ddebf56
2024-10-29 22:56:29 +00:00
Alex Orlenko ddebf56b41 Defer metatable return on userdata creation until the end
Relates to #477
2024-10-29 21:22:34 +00:00
Alex Orlenko 4f56575e05 v0.10.0 2024-10-25 13:36:55 +02:00
Alex Orlenko 35fa76263e Update docs 2024-10-25 11:05:21 +02:00
Alex Orlenko 446d63a77e More tests 2024-10-23 15:50:58 +01:00
Alex Orlenko 8d8d521721 Add error-send feature flag 2024-10-23 10:40:25 +01:00
Alex Orlenko 0d31a1caa6 Rename Error::MemoryLimitNotAvailable to Error::MemoryControlNotAvailable 2024-10-22 22:44:52 +01:00
Alex Orlenko 3dc58cdfc9 Move Luau Vector type to top level 2024-10-22 22:41:26 +01:00
Alex Orlenko 5724b5f112 cargo fmt 2024-10-20 13:23:57 +01:00
Alex Orlenko d64d9719c6 Replace Either enum with implementation from either crate 2024-10-20 12:06:08 +01:00
Alex Orlenko 75475fc9a8 Add missing serde::{de, ser} top level comment 2024-10-20 11:52:13 +01:00
Alex Orlenko f8fe9246bb Bump TARGET_MLUA_LUAU_ABI_VERSION 2024-10-20 10:42:59 +01:00
Alex Orlenko 5c54361236 Keep stack in FromLuaMulti::from_stack_multi 2024-10-20 00:47:20 +01:00
Alex Orlenko ec227f9056 Optimize Table readonly check (Luau)
Optimize `Table::has_metatable` check.
2024-10-19 23:58:57 +01:00
Alex Orlenko 93a1a55aaa Update docs 2024-10-19 23:10:43 +01:00
Alex Orlenko a020b2b5b2 Remove functions deprecated in v0.9 2024-10-19 15:14:09 +01:00
Alex Orlenko 2c756e5958 Add back Lua::load_from_std_lib (with deprecated flag) 2024-10-19 15:13:42 +01:00
Alex Orlenko c702077028 More Lua values conversion tests 2024-10-19 15:08:51 +01:00
Alex Orlenko 08545224f4 clippy 2024-10-19 11:49:40 +01:00
Alex Orlenko e122f90837 More Either tests 2024-10-19 11:20:25 +01:00
Alex Orlenko c638d90b02 Fix test_inspect_stack 2024-10-19 00:11:05 +01:00
Alex Orlenko 930fd9c00f Fix Value::String::to_pointer for Lua < 5.4 2024-10-18 23:12:04 +01:00
Alex Orlenko 2a8db87132 Update Value tests 2024-10-18 22:50:48 +01:00
Alex Orlenko cbae4fe59c More async tests 2024-10-18 22:50:15 +01:00
Alex Orlenko c68e3c4f41 Some DebugStack improvements 2024-10-18 22:48:41 +01:00
Alex Orlenko 02d4ceff34 Make Thread::state non-const (private api) 2024-10-18 21:50:57 +01:00
Alex Orlenko 98339c57e6 Update userdata tests 2024-10-18 21:38:02 +01:00
Alex Orlenko 2331995e28 Update coverage ci options 2024-10-18 21:36:49 +01:00
Alex Orlenko c07bdce250 More scope tests 2024-10-17 17:03:31 +01:00
Alex Orlenko 084a85c3d8 Update error tests 2024-10-16 23:57:48 +01:00
Alex Orlenko 735aa22be9 Fix typo in chunk tests 2024-10-16 23:57:07 +01:00
Alex Orlenko 5479546b27 Update chunk tests 2024-10-16 21:54:22 +01:00
Alex Orlenko 179c54f297 Remove generic from Table::equals and Value::equals 2024-10-16 16:11:48 +01:00
Alex Orlenko f9ae4bf05f Update table tests 2024-10-16 16:00:01 +01:00
Alex Orlenko 9e16e18132 Update string tests 2024-10-16 15:59:23 +01:00
Alex Orlenko 7535a23fa2 Update function tests 2024-10-16 15:59:13 +01:00
Alex Orlenko 3787ff9e8c Optimize metatable pointer lookup for userdata (Luau) 2024-10-12 21:38:07 +01:00
Alex Orlenko 0a2a70c15a mlua-sys: v0.6.4 2024-10-12 21:32:08 +01:00
Alex Orlenko 81d7c81532 Update Luau to 0.647 2024-10-12 21:30:56 +01:00
Alex Orlenko 0453029765 v0.10.0-rc.1 2024-10-08 23:00:04 +01:00
Alex Orlenko 7b777d074e Update README 2024-10-08 22:57:32 +01:00
Alex Orlenko c6cd1c53c3 Update CHANGELOG 2024-10-08 22:57:18 +01:00
Alex Orlenko 669349d704 mlua_derive: v0.10.0-rc.1 2024-10-08 22:40:07 +01:00
Alex Orlenko c086c144d0 Use impl IntoIterator in Lua::create_table_from/create_sequence_from 2024-10-08 11:53:31 +01:00
Alex Orlenko 640cb2c182 Add _unguarded to RawLua::app_data_ref (for internal use only) 2024-10-07 13:37:41 +01:00
Alex Orlenko 03a4068d55 Move MultiValue from value to multi module 2024-10-07 13:37:39 +01:00
Alex Orlenko 9f6c78532f Move IntoLua/FromLua and IntoLuaMulti/FromLuaMulti to traits module 2024-10-07 10:32:50 +01:00
Alex Orlenko 8aecc83f53 clippy 2024-10-06 22:48:58 +01:00
Alex Orlenko 4891b6535c Remove const from Value::type_name 2024-10-06 22:45:56 +01:00
Alex Orlenko fa343c2c69 More optimal OsStr/Path conversion to Lua 2024-10-06 22:45:36 +01:00
psentee 6d5e735bed Add IntoLua/FromLua for OsString/OsStr and PathBuf/Path (#459) 2024-10-06 13:43:20 +01:00
Alex Orlenko ac315fd80b Add AnyUserData::wrap_ser function 2024-10-06 11:12:44 +01:00
Alex Orlenko f95161c6e0 Add missing documentation for Function::wrap_raw* functions 2024-10-05 23:26:03 +01:00
Alex Orlenko 4bc846a119 Add optional anyhow dependency (under the same feature flag) to implement IntoLua for anyhow::Error 2024-10-05 23:16:36 +01:00
Alex Orlenko a3ca95fc8f Include Value::Other variant into Value::to_pointer() helper.
Closes #465
2024-10-04 10:33:56 +01:00
Alex Orlenko ae4897ab2e Add Value::is_error and Value::as_error helpers 2024-10-02 12:59:04 +01:00
Alex Orlenko 4ac87c7208 Derive PartialEq instead of implementing manually 2024-10-02 12:21:04 +01:00
Alex Orlenko 04d8106676 Remove SubtypeId from AnyUserData and instead add Value::Other variant that will cover any unknown types (eg. LuaJIT CData) 2024-10-02 12:16:48 +01:00
Alex Orlenko b6cdf32f16 Update MSRV in README 2024-10-01 23:21:28 +01:00
Alex Orlenko ad9bc36764 impl Eq/Ord for Lua String 2024-10-01 23:20:19 +01:00
Alex Orlenko 4b8c26e682 Invoke __tostring metamethod when calling Value::to_string() for Buffer type 2024-10-01 22:59:19 +01:00
Alex Orlenko 7839c4438c Fix compilation warnings 2024-10-01 22:28:47 +01:00
Alex Orlenko 529361fcbc Add new Buffer type for Luau.
Previously it was represented as `AnyUserData` which is not always convenient.
2024-10-01 15:21:19 +01:00
Alex Orlenko 4dddf3c18d Use fmt::Debug implementation for Lua string from bstr 2024-09-26 22:46:02 +01:00
Alex Orlenko fb0c0d9ee9 Add Either<L, R> enum to combine two types into a single one.
It implements `FromLua` and `IntoLua` traits for easy type conversions..
2024-09-26 18:58:28 +01:00
Alex Orlenko 235c32006c Rename Lua::with_raw_state to Lua::exec_raw 2024-09-24 23:11:16 +01:00
Alex Orlenko b65901e444 Add Error::chain method to return iterator over nested errors 2024-09-24 22:48:19 +01:00
Alex Orlenko 91fe02da45 Add LuaNativeFn/LuaNativeFnMut/LuaNativeAsyncFn traits for using in Function::wrap 2024-09-24 14:35:03 +01:00
Alex Orlenko 8274b5fa88 More user-friendly error message on userdata mismatch 2024-09-23 15:56:29 +01:00
Alex Orlenko 762e677a70 Update Error matching code
This is mostly cosmetic change.
2024-09-23 15:53:08 +01:00
Alex Orlenko 5b5f1e4669 Remove undocumented Lua::push in favour of Lua::with_raw_state 2024-09-23 11:14:24 +01:00
Alex Orlenko e582e7c57f Rename get_metatable to metatable for Table/AnyUserData types 2024-09-23 11:13:16 +01:00
Alex Orlenko 3714da5ec8 Fix doc test for Lua::set_type_metatable 2024-09-23 11:01:32 +01:00
Alex Orlenko ca69be07ff Support setting metatable for Lua builtin types.
Closes #445
2024-09-23 10:45:57 +01:00
Alex Orlenko 16951e3628 Move ValueRef to a new module 2024-09-22 23:58:53 +01:00
Alex Orlenko 8bb2b444ab Run tests with forced memory limit checks 2024-09-22 23:20:18 +01:00
Alex Orlenko fc1570d2d7 Support yielding from hooks for Lua 5.3+ 2024-09-22 19:04:32 +01:00
Alex Orlenko fce85381c6 Fix clippy warnings 2024-09-22 19:02:16 +01:00
Alex Orlenko 3088516851 Update luaL_checkstack messages 2024-09-22 17:33:28 +01:00
Alex Orlenko 5162a0f46e Add Lua::with_raw_state to provide easy low-level access to the Lua state. 2024-09-22 11:25:38 +01:00
Alex Orlenko 640d27697d Fix compile error in non-send mode 2024-09-20 21:20:52 +01:00
Alex Orlenko da4404baa5 Add Lua::scope back 2024-09-20 13:01:55 +01:00
Alex Orlenko 7c2e9b5a7c v0.10.0-beta.2 2024-09-07 23:47:21 +01:00
Alex Orlenko 5db545e7b4 mlua_derive: v0.10.0-beta.1 2024-09-07 23:46:13 +01:00
Alex Orlenko b88228b3d4 Update CHANGELOG 2024-09-07 23:43:20 +01:00
Alex Orlenko 8677b57847 Update README 2024-09-07 23:27:35 +01:00
Alex Orlenko 7543b0674e mlua-sys: v0.6.3 2024-09-07 23:23:47 +01:00
Alex Orlenko 8e111058c3 Make BorrowedBytes/BorrowedStr: Send + Sync
They are immutable and don't require holding a lock
2024-09-07 23:17:25 +01:00
Alex Orlenko e1c0aa8491 Fix test test_integer_from_lua 2024-09-07 17:01:04 +01:00
Alex Orlenko 7957c6868d Fastpath for LuaString/integer/float conversion from Lua 2024-09-07 12:25:35 +01:00
Caleb Maclennan 9c86eefb76 Update documentation of traits to match the expected argument name (#447) 2024-09-06 19:28:40 +01:00
Alex Orlenko 7272e40c23 Faster non-scoped callbacks 2024-09-03 01:34:55 +01:00
Alex Orlenko c6ef393ce9 Turn Lua::entrypoint() to constructor (don't require initializing Lua first) 2024-09-02 23:43:52 +01:00
Alex Orlenko d25f2fc07c Take Table instead of impl IntoLua in Chunk::set_environment() 2024-09-01 23:06:27 +01:00
Alex Orlenko 104e242ddd Some cosmetic changes 2024-09-01 22:48:56 +01:00
Alex Orlenko 825bdbfa04 Use dynamic Lua state ownership instead of compile-time module cfg flag to allow
creating new VMs in module mode (and destructing them properly).
2024-08-31 22:52:55 +01:00
Alex Orlenko 1634c43f0a Disable send feature in module mode
We don't have exclusive access to Lua VM and cannot provide `Sync` soundness
2024-08-30 23:37:26 +01:00
Alex Orlenko d6b27de34e Optimize ObjectLike::to_string for tables and userdata 2024-08-30 22:55:53 +01:00
Alex Orlenko 4018a17e26 Combine TableExt and AnyUserDataExt traits into ObjectLike 2024-08-30 22:50:03 +01:00
Alex Orlenko 5ebbc0868c More inline const expressions 2024-08-29 22:05:28 +01:00
Alex Orlenko 3774296835 Run gargabe collection on main Lua instance drop
This should help preventing leaking memory when capturing Lua in async block
and dropping future without finishing polling.
2024-08-29 11:59:52 +01:00
Alex Orlenko ece66c46bf Remove unstable feature flag 2024-08-26 23:51:58 +01:00
Alex Orlenko 66b4a865c2 Remove MultiValue pool 2024-08-26 11:26:12 +01:00
Alex Orlenko 21149106ee Remove drop field from ValueRef 2024-08-26 11:18:17 +01:00
Alex Orlenko 74bebe6da3 Add optional Send requirement to internall callbacks 2024-08-26 11:13:06 +01:00
Alex Orlenko 9891e86d16 Remove Clone requirement from UserDataFields::add_field() 2024-08-26 11:10:34 +01:00
Alex Orlenko e3c5cfdf19 Extract registry_key and vector modules from types 2024-08-26 00:27:41 +01:00
Alex Orlenko 4977b91a98 Detect compilation error and return Result when using Compiler::compile() interface.
Closes #387
2024-08-25 23:07:13 +01:00
Alex Orlenko 6317b8e0c8 Test GC for nested userdata (userdata in userdata) 2024-08-25 21:02:12 +01:00
Alex Orlenko ecc09c4387 Add luaL_loadbufferenv helper to all Lua versions 2024-08-25 17:29:58 +01:00
bjcscat 7bfd32750d Change chunk env to use luau's load env parameter (#442) 2024-08-25 16:50:41 +01:00
Alex Orlenko 23d4e2519b Switch to Mutex from RwLock for userdata access in send mode.
Unfortunately RwLock allow access to the userdata from multiple threads
without enforcing `Sync` marker.
2024-08-24 09:44:39 +01:00
Alex Orlenko 2857cb76c6 Add A param to AsyncThread<A, R>.
This reduces internal dependency on `MultiValue` container and delay args conversion to the future `poll()` stage.
2024-08-23 01:04:06 +01:00
Alex Orlenko fdc50bffc9 Fix memory leak when polling async futures 2024-08-23 01:03:54 +01:00
Alex Orlenko 9931709ecd Remove explicit lifetime from UserDataMethods and UserDataFields traits.
Pass `'static` arguments to async functions and require `'static` Future.
(in future we can use async closures to make it more elegant).
2024-08-23 00:10:29 +01:00
Alex Orlenko 8092f00930 Do not require Lua to be alive when dropping AsyncThread 2024-08-22 23:41:51 +01:00
Alex Orlenko d2e87943ac Skip extra lock when resuming thread 2024-08-22 23:41:50 +01:00
Sven Niederberger 26b9bdb362 serde_userdata: Remove map_err to reduce compile time impact (#441) 2024-08-21 12:06:49 +01:00
Alex Orlenko c58f67b140 Add MaybeSend requirement to Lua futures 2024-08-10 17:55:39 +01:00
Sven Niederberger 0c08cdaf7c Reduce compile time contribution of next_key_seed and next_value_seed (#436)
* factor out common code

* changelog entry
2024-08-06 20:04:17 +01:00
Alex Orlenko 10999babe0 Replace MultiValue::extend_from_values with from_lua_iter 2024-08-06 01:32:20 +01:00
Alex Orlenko f0a995a357 Make MultiValue::with_capacity public 2024-08-05 23:36:17 +01:00
Alex Orlenko aa47324ee9 Use pool for MultiValue container 2024-08-05 22:07:19 +01:00
Alex Orlenko ac6a391426 Remove Lua::into_static and Lua::from_static (undocumented). 2024-08-05 14:57:15 +01:00
Alex Orlenko 8e14b6e40b Fix tests 2024-08-04 22:22:02 +01:00
Alex Orlenko c117a4c1af Fix loading (fetching) stdlib modules when using require in Luau.
Fixes #435.
2024-08-04 18:55:17 +01:00
Alex Orlenko 4082b354fe clippy 2024-08-01 00:55:10 +01:00
Alex Orlenko 3641c98959 Prepare for Rust 2024 edition (see rust-lang/rust#123748)
Replace `IntoLua(Multi)` generic with positional arg (impl trait) where possible
This allow to shorten syntax from `a.get::<_, T>` to `a.get::<T>`
2024-07-31 23:42:43 +01:00
Alex Orlenko b7d170ab9b Refactor ThreadStatus:
- Add `ThreadStatus::Running`
- Replace `ThreadStatus::Unresumable` with `ThreadStatus::Finished`
Change `Error::CoroutineInactive` to `Error::CoroutineUnresumable`
2024-07-31 22:34:45 +01:00
Alex Orlenko 5acf9d758d Update CHANGELOG for v0.10.0-beta.1 2024-07-31 13:42:39 +01:00
Alex Orlenko 833790967b Update async examples (use send feature) 2024-07-31 13:42:39 +01:00
Alex Orlenko a86d6ab330 Mark LightUserData as Send+Sync (send feature flag) 2024-07-31 13:42:39 +01:00
Alex Orlenko 2f8755dcc7 Update README 2024-07-31 13:42:38 +01:00
Alex Orlenko 94415065c0 Fix Lua String soundness when borrowing &str or &[u8].
Make borrowing using new `BorrowedStr` and `BorrowedBytes` types that holds strong reference to Lua.
2024-07-31 13:42:38 +01:00
Alex Orlenko bba644e83f Fix compilation 2024-07-31 13:42:38 +01:00
Alex Orlenko d5173380e3 Split util module
Refactor internal userdata types to switch to the new `TypeKey` trait
2024-07-31 13:42:38 +01:00
Alex Orlenko d9941ef409 Take &mut RegistryKey in Lua::replace_registry_value. 2024-07-31 13:42:38 +01:00
Alex Orlenko c715aec1f7 Do not consume self in Table::pairs and Table::sequence_values methods. 2024-07-31 13:42:38 +01:00
Alex Orlenko cd3f45f31f Rust nightly rustfmt 2024-07-31 13:42:37 +01:00
Alex Orlenko b4892c228c Add rustfmt.toml 2024-07-31 13:42:37 +01:00
Alex Orlenko 7a75c73052 Move AppData to types::app_data mod 2024-07-31 13:42:37 +01:00
Alex Orlenko 658f2a13ea Support multi threads under send feature flag 2024-07-31 13:42:37 +01:00
Alex Orlenko c1395ab543 clippy 2024-07-31 13:42:37 +01:00
Alex Orlenko cd6d86a5ce Split single lua module to multiple submodules under state 2024-07-31 13:42:37 +01:00
Alex Orlenko 8b2d067196 Fix Lua::set_vector_metatable 2024-07-31 13:42:36 +01:00
Alex Orlenko 07a5538e50 Don't run destructors if Lua is gone 2024-07-31 13:42:36 +01:00
Alex Orlenko d17dc54645 Rewrite call_async in traits to use impl Future instead of LocalBoxFuture.
More use `std::future` types instead of `future-utils`.
2024-07-31 13:42:36 +01:00
Alex Orlenko baa8895cfb Optimize creation of userdata callbacks (registry) 2024-07-31 13:42:36 +01:00
Alex Orlenko 550d6b2991 Remove wrapped UserData impl (Rc/Arc/etc) 2024-07-31 13:42:36 +01:00
Alex Orlenko b3649a44e0 Move userdata-related top-level modules into the userdata module 2024-07-31 13:42:36 +01:00
Alex Orlenko 313117095c Remove parking_lot feature flag 2024-07-31 13:42:35 +01:00
Alex Orlenko 7221051683 Refactor:
- Remove the rest of `'lua` lifetimes
- Remove Owned types
2024-07-31 13:42:35 +01:00
Alex Orlenko 1eb2ecb3b0 Drop lifetime from IntoLua 2024-07-31 13:42:35 +01:00
Alex Orlenko 24b0672d99 Rename LuaRef to ValueRef 2024-07-31 13:42:35 +01:00
Alex Orlenko 08c7429531 Optimize various parts of the code to remove unnecessary clone calls.
This is became possible after implementing `IntoLua` for references.
2024-07-31 13:42:35 +01:00
Alex Orlenko aa05eb4c81 Switch MultiValue to use VecDeque under the hood. 2024-07-31 13:42:35 +01:00
Alex Orlenko 4aa178fcc0 Take &Value in Lua::push_value() 2024-07-31 13:42:35 +01:00
Alex Orlenko 98ca880f8a Change Value::Error variant to store Box<Error> instead of Error.
This will help to reduce size of `Value` enum on the stack.
2024-07-31 13:42:34 +01:00
Alex Orlenko fe21ef43ba Replace AtomicPtr with Cell 2024-07-31 13:42:34 +01:00
Alex Orlenko 7d3704c222 mlua-sys: v0.6.2 2024-07-25 22:49:56 +01:00
Alex Orlenko 496c8fa0e9 Fix references to master branch 2024-07-25 22:49:32 +01:00
Alex Orlenko 9ce0e11518 Use Lua 5.4.7 2024-07-25 22:45:40 +01:00
Caleb Maclennan b77836920a Link to other mlua projects published to LuaRocks (#425)
* Add a link to decasify, another mlua success story published on LuaRocks

* Add a few more downstream project links that seem reasonably maintained
2024-07-04 23:31:05 +01:00
Alex Orlenko f1ceaf0ff1 v0.9.9 2024-06-18 16:09:08 +01:00
Alex Orlenko 884a025b52 Fix some clippy warnings 2024-06-18 16:09:08 +01:00
Alex Orlenko c23fa5aa6c Optimize RegistryKey internals
- Store single `AtomicI32` field instead of pair i32,AtomicBool
- Make creation faster by skipping intermediate `Value` layer
Add new `RegistryKey::id()` method to return underlying identifier
2024-06-18 16:09:06 +01:00
Alex Orlenko 4f1d2abbcb Bump rustc-hash to 2.0 2024-06-18 11:18:55 +01:00
Alex Orlenko a25e81036e Do not allow already running coroutines to be reset or resumed.
This is a wrong use of the Lua API and is not supported.
See #416 for the reference.
2024-06-12 23:18:46 +01:00
Alex Orlenko b46cad1db1 Support Luau v0.629 2024-06-09 00:37:58 +01:00
Alex Orlenko f2d48ce296 v0.9.8 2024-05-15 23:17:30 +01:00
Alex Orlenko 0fa39d431d mlua_derive: v0.9.3 2024-05-15 23:14:23 +01:00
Alex Orlenko 0aa86c4d47 Update CHANGELOG 2024-05-15 23:11:50 +01:00
Alex Orlenko ea2faa3755 clippy 2024-05-15 23:11:33 +01:00
Alex Orlenko 59c9abbac7 Fix serializing same table multiple times.
Fixes #408
2024-05-14 00:42:04 +01:00
Alex Orlenko 8f3de8aa19 mlua-sys: v0.6.0 2024-05-04 21:22:17 +01:00
Alex Orlenko 317ce7caa6 Add Lua::set_fflag() to control Luau feature flags 2024-05-04 21:15:22 +01:00
Alex Orlenko 3a44729a48 Mark lua_Callbacks as non exhaustive (Luau) 2024-05-04 21:11:29 +01:00
Alex Orlenko 3d46fad459 Update luau-src to v0.9
Mark `lua_CompileOptions` as non exhaustive
2024-05-04 21:10:24 +01:00
Joris Willems ffc4bd599c Fix module imports for export (#394) 2024-04-18 10:22:41 +01:00
Eric Stokes 1c969da286 update build script to fix cross compilation of windows dlls from unix (#397) 2024-04-17 20:29:41 +01:00
Alex Orlenko 45fd2fa40a mlua-sys: v0.5.2 2024-04-05 14:04:15 +01:00
Alex Orlenko 806bd202d6 v0.9.7 2024-04-05 12:46:12 +01:00
Alex Orlenko a644049087 Cosmetic changes for clippy 2024-04-05 12:44:18 +01:00
Alex Orlenko ad31bed1db Minor improvements in serializing:
Use `&str` instead of creating Lua string when serializing tuple variant and struct variant
2024-04-05 12:44:08 +01:00
Alex Orlenko 62f0bb97b0 Add Lua::create_ser_any_userdata() function 2024-04-05 12:03:40 +01:00
Alex Orlenko f67f8646ae Implement push_into_stack/from_stack for Option<T> 2024-03-28 18:18:08 +00:00
Alex Orlenko fa217d3706 Better Luau buffer type support.
- Add `Lua::create_buffer()` function
- Support serializing buffer type as a byte slice
- Support accessing copy of underlying bytes using `BString`
2024-03-28 13:05:01 +00:00
Yiyu Lin b62f2ee0f7 chore: make clippy happy (#388)
Co-authored-by: hzlinyiyu <hzlinyiyu@corp.netease.com>
2024-03-25 13:26:06 +00:00
Alex Orlenko 6e6c73e4c7 Add deserialize json benchmark 2024-03-23 22:31:42 +00:00
Alex Orlenko 508517c45e Do not use dependencies as implicit features.
Add `dep:` prefix instead.
2024-03-23 19:47:42 +00:00
Alex Orlenko a79840afc9 Suppress Rust 1.77 dead_code false warnings. 2024-03-23 11:42:46 +00:00
Alex Orlenko 39afe4c6f7 Add SerializeOptions::detect_serde_json_arbitrary_precision to detect serde_json::Number with arbitrary_precision and convert it to Lua number.
By default the option is disabled and such numbers represented as Lua objects with `$serde_json::private::Number` key.
Fixes #385
2024-03-23 11:27:46 +00:00
Alex Orlenko 038cc5f974 Bump rustyline to 14.0 2024-03-22 00:40:21 +00:00
Alex Orlenko 59b14000f3 Update hyper examples 2024-03-22 00:35:37 +00:00
19年梦醒 849206ef9d update hyper to v1, and add shell command example (#384)
Co-authored-by: Alex Orlenko <zxteam@protonmail.com>
2024-03-21 23:06:00 +00:00
Alex Orlenko 80fff4f2e7 Update reqwest to 0.12 2024-03-21 22:29:34 +00:00
Alex Orlenko 9734146313 Add Function::deep_clone() (Luau only) 2024-03-21 22:14:00 +00:00
Alex Orlenko 58be624222 Remove redundant "match" when checking userdata type via AnyUserData::is.
Fixes #386
2024-03-20 19:29:47 +00:00
Alex Orlenko 3d43103431 Make __idiv metamethod available for luau
Closes #383
2024-03-20 19:20:05 +00:00
Alex Orlenko 5a22437d5f Assert that luau_compile returns non-null pointer.
Fixes #381
2024-03-16 23:39:10 +00:00
Alex Orlenko 83c075c72b Implement IntoLua for RegistryKey 2024-02-29 22:45:32 +00:00
Alex Orlenko 270b98a429 v0.9.6 2024-02-29 15:39:14 +00:00
Alex Orlenko 0ee3324462 Add LUA_TCDATA to util::to_string() helper 2024-02-29 12:57:04 +00:00
Alex Orlenko 8a9c4f0b15 Optimize table array traversal during serialization 2024-02-11 23:02:36 +00:00
Alex Orlenko 34db5f985e Refactor benchmarks 2024-02-11 17:48:25 +00:00
Alex Orlenko 020e8a78a8 Impl FromLua for RegistryKey 2024-02-10 15:41:48 +00:00
Alex Orlenko 3ca7b4942e Implement IntoLua for &Value 2024-02-07 00:10:49 +00:00
Alex Orlenko 1754226c74 Impl IntoLua::push_into_stack for integers 2024-02-03 21:32:49 +00:00
Alex Orlenko 3014c4d7a1 Add REF_STACK_RESERVE constant 2024-02-02 23:26:33 +00:00
Alex Orlenko 908f37656a Add to_pointer function to Function/Table/Thread 2024-02-02 09:23:16 +00:00
Alex Orlenko f4d783cb41 Impl push_into_stack for StdResult 2024-02-01 23:42:44 +00:00
Alex Orlenko f5982bc204 Add inline to FromLua<bool>::from_stack 2024-01-27 14:40:32 +00:00
Alex Orlenko e30b425224 Fix crash when initializing Luau sandbox without stdlibs (#361) 2024-01-27 11:51:59 +00:00
Alex Orlenko 512921404c Add fastpath push_into_stack/from_stack methods for bool type 2024-01-26 14:07:56 +00:00
Alex Orlenko dfd82edc42 Add Lua::push() helper 2024-01-26 13:52:19 +00:00
Alex Orlenko 75a15ceabf v0.9.5 2024-01-25 22:24:58 +00:00
Alex Orlenko 60730fd068 Update compile tests messages 2024-01-25 18:19:16 +00:00
Alex Orlenko df778b7b33 Impl Into/FromLua for OwnedString 2024-01-25 18:04:55 +00:00
Alex Orlenko 45299c0ef1 Update authors 2024-01-25 12:56:57 +00:00
Alex Orlenko 38eec1236c Update itertools dependency 2024-01-25 10:49:39 +00:00
Alex Orlenko 145c5b316b Fix FromLua derive proc macro to cover more cases 2024-01-25 10:26:43 +00:00
Alex Orlenko e97e69a309 Update Luau to 0.609 (luau-src v0.8.0) 2024-01-25 09:38:46 +00:00
Alex Orlenko 2ac7b23596 Impl Into/FromLua for OwnedThread 2024-01-25 09:34:37 +00:00
Alex Orlenko 8200bee467 Implement IntoLua for ref to String/Table/Function/AnyUserData
This would prevent cloning plus has better performance when pushing values to Lua stack (`IntoLua::push_into_stack` method)
2024-01-23 22:14:50 +00:00
Alex Orlenko fe6ab250bf Update codecov links after moving repo 2024-01-23 21:56:00 +00:00
Alex Orlenko 804972b099 Fix typos in examples/guided_tour 2024-01-23 20:50:57 +00:00
Alex Orlenko 727f99ee4d Implement IntoLua for &RegistryKey
This would allow just passing registry keys to arguments with fasttrack to push directly into stack.
2024-01-20 22:04:28 +00:00
Alex Orlenko 3c801e7b17 Expose internal POLL_PENDING constant (hidden) 2024-01-20 15:52:00 +00:00
Alex Orlenko a38e484fe9 Increase luau max stack size to 1M from 100k 2024-01-18 23:05:23 +00:00
Alex Orlenko eed48889cd v0.9.4 2024-01-10 15:41:31 +00:00
Alex Orlenko b5896173fd Include skip_memory_check code only when the corresponding attribute set for module 2024-01-10 10:08:33 +00:00
Alex Orlenko 205989f569 Fix edge case when loading many-in-one module from thread without using its state.
If Lua previously been initialized in main thread and then new module was loaded from thread
we reuse old state which confuses Lua loader.
2024-01-10 00:37:02 +00:00
Alex Orlenko 12472de1d2 v0.9.3 2024-01-08 21:02:45 +00:00
Alex Orlenko a68708c12e Update README & CHANGELOG 2024-01-08 18:44:49 +00:00
Alex Orlenko 4c0474d573 Fix docsrs attr for Thread::reset 2024-01-06 15:04:18 +00:00
Alex Orlenko 60e859f643 Fix (nightly) warning in doc 2024-01-06 12:55:18 +00:00
Alex Orlenko 9ed0d90746 Run tests for wasm32-unknown-emscripten 2024-01-06 12:12:56 +00:00
Alex Orlenko 514ec24252 Fix lua53/lua54 luaL_error definition (for wasm32) 2024-01-03 12:02:53 +00:00
Alex Orlenko 244e6c9c12 Bump rustyline dependency 2024-01-03 11:34:50 +00:00
Alex Orlenko 4749e3a22a Update minimal lua(u) versions (needed for wasm32) 2024-01-03 11:34:34 +00:00
Alex Orlenko cf153f38de Panic when try to build for wasm32 without vendored feature (except luau) 2024-01-03 11:33:42 +00:00
Alex Orlenko c0a0983025 mlua-sys: always inline lua_error 2024-01-03 11:31:39 +00:00
Alex Orlenko 69ff0c5509 mlua-sys: fix Lua 5.2 lua_sethook definition 2024-01-03 10:22:42 +00:00
Aymen-Hakim bf79d6c212 Update lauxlib.rs (#351)
lua54 in lua53 src.
2023-12-27 15:07:59 -05:00
ByteDream 0b9a85e183 Add lua emscripten support (#338) 2023-12-14 14:54:57 +00:00
Alex Orlenko 59974d7bde Merge pull request #337 from tari/emscripten-support
Fix build for emscripten target
2023-12-13 13:55:26 +00:00
Joel Natividad 61e846326c Update Cargo.toml (#342) 2023-12-10 17:04:37 +00:00
Alex Orlenko 3547985bb0 Merge pull request #339 from eatradish/fix-loongarch64-build
Add loongarch64 architecture support
2023-12-08 09:13:07 +00:00
eatradish 4c92580201 Add loongarch64 architecture support 2023-12-08 11:16:52 +08:00
Peter Marheine e3f34f319c Correct C return type for lua_error
The definition of lua_error in all of Lua 5.1 through 5.4 says lua_error
returns int, but the Rust definition of the same function treats it as
void (because it's known not to return). This causes link-time errors when
building for wasm targets because the wasm linker is aware of function return
types and errors out if they differ between definition and declaration.
2023-12-06 21:16:52 +11:00
Peter Marheine b16f3895a0 lua54: use changed return type for lua_rawlen
Lua 5.4 changed lua_rawlen to return lua_Unsigned, versus size_t in earlier
versions. Change the C API signature to match, and add a wrapper function with
the same name that maintains a stable Rust API by casting to usize.
2023-12-06 21:09:25 +11:00
Alex Orlenko a4c919231c Don't clone function name when calling async userdata method 2023-12-03 19:55:27 +00:00
Alex Orlenko e4d6e92287 (async) Move "pending" poll value from env to poll_future() results 2023-12-02 15:01:40 +00:00
Alex Orlenko 642201a7e0 Remove locals from __mlua_async_poll helper 2023-12-01 18:11:06 +00:00
Alex Orlenko c36808b251 Faster Function::call() for lua51/jit/luau 2023-12-01 12:03:46 +00:00
Alex Orlenko 2022de2156 v0.9.2 2023-11-21 22:35:59 +00:00
Alex Orlenko 66e01548ce Update Luau+windows require dylib failed test 2023-11-20 22:16:44 +00:00
Alex Orlenko 93b505cff9 Integrate Luau package into mlua api.
Eg. `Lua::load_from_std_lib` with `StdLib::PACKAGE` is now supported for Luau.
2023-11-20 12:08:08 +00:00
Alex Orlenko 44f5688c32 Include luau to ci module tests 2023-11-16 17:54:40 +00:00
Alex Orlenko 2bee5ed33a Support binary modules for Luau on cfg(unix) 2023-11-16 15:54:25 +00:00
Alex Orlenko 2d775695ef Rewrite Luau require function to support module loaders.
Also add `package` library with `path`/`loaded`/`loaders`.
2023-11-16 14:39:17 +00:00
Alex Orlenko 34476ebf53 Support LuaJIT cdata type (produced by ffi module) 2023-11-16 13:33:23 +00:00
Alex Orlenko 5043447f23 Support Luau buffer type and and library.
Buffer is an object that represents a fixed-size mutable block of memory and added to Luau 0.601.
See https://luau-lang.org/library#buffer-library for more details.
2023-11-16 12:55:58 +00:00
Alex Orlenko b879abc418 Add lua_newuserdata_t helper to mlua-sys/luau 2023-10-24 23:58:47 +01:00
Alex Orlenko a1e39a8620 Remove MemoryState from ExtraData and retrieve using lua_getallocf (recently added to Luau) 2023-10-24 00:23:32 +01:00
Alex Orlenko f5021daaea Fix typo in table_traversal_sequence becnhmark 2023-10-12 11:07:00 +01:00
Alex Orlenko ec0fb7614e Optimize Table serialization (faster traversal) 2023-10-12 10:37:52 +01:00
Alex Orlenko 04c0763146 Optimize iterating over array part of table 2023-10-12 09:52:34 +01:00
Alex Orlenko 7d0aa7c01b Add "table traversal sequence" benchmark 2023-10-12 09:49:36 +01:00
Alex Orlenko 2a3980ef62 Add Table::for_each method for fast (faster than pairs()) table pairs traversal. 2023-10-10 12:52:26 +01:00
Alex Orlenko adb979761e Optimize table traversal (pairs iterator). 2023-10-10 11:25:21 +01:00
Alex Orlenko 37d722d71c Add table traversal benchmark 2023-10-10 11:00:58 +01:00
Alex Orlenko 6e3af593f6 Add serde benchmark 2023-10-10 00:25:47 +01:00
Alex Orlenko 54c14cc5b8 Update luajit (vendored) to 210.5.0
This includes switch from openresty's to vanilla luajit
2023-09-29 12:57:29 +01:00
Alex Orlenko 7f0de5790a Update github CI actions/checkout to v4 2023-09-13 12:03:50 +01:00
Alex Orlenko 6488477bc4 rustfmt 1.72+ 2023-08-27 23:26:27 +01:00
Alex Orlenko 53b7b5b70b Fix setting userdata (static) fields when it has __index metamethod/function.
Closes #312
2023-08-27 23:01:37 +01:00
Alex Orlenko fc159e0c46 v0.9.1 2023-08-24 01:41:32 +01:00
Alex Orlenko a802276c56 Fix an edge case when using invalidated (relative) userdata index after processing varargs.
This causes Lua API correctness check assertion in debug mode.
Fixes #311.
2023-08-24 00:54:50 +01:00
Alex Orlenko 65b816f2f0 Update README 2023-08-21 22:00:04 +01:00
Alex Orlenko e2b3464ec9 impl IntoLuaMulti for StdResult<(), E> 2023-08-20 14:17:18 +01:00
Alex Orlenko 89cf5bf362 impl Default for Lua 2023-08-20 12:16:11 +01:00
Alex Orlenko bb0a6070c4 v0.9.0 2023-08-17 11:38:42 +01:00
Alex Orlenko 60f1c16ddb mlua-sys: v0.3.2 2023-08-16 14:59:16 +01:00
Alex Orlenko 17809a390c mlua_derive: v0.9.0 2023-08-16 01:25:12 +01:00
Alex Orlenko 7662a7e4ff Update non-static (scoped) userdata:
- Use the new stack api
- Support static fields constructor
- Better error messages (on bad arguments)
2023-08-15 01:22:57 +01:00
Alex Orlenko d769a95fc5 Add Scope::create_any_userdata() 2023-08-14 17:39:51 +01:00
Alex Orlenko 0e4476c2e3 Add Lua::set_vector_metatable() method (unstable) 2023-08-12 22:46:21 +01:00
Alex Orlenko d48a2b3f6c Add OwnedThread type (unstable) 2023-08-12 21:18:12 +01:00
Alex Orlenko b3592bc23e Update mutable_globals pointer type to const (luau 0.590) 2023-08-12 17:43:25 +01:00
Alex Orlenko 052310e93d Update doc for deny_unsupported_types option 2023-08-12 17:40:39 +01:00
Alex Orlenko 09eb7f251b Support options for Value::serialize() implementation
To match `lua.from_value_with()` functionality.
2023-08-12 17:24:57 +01:00
Alex Orlenko c137da7618 Update to Luau 0.590 2023-08-12 14:12:59 +01:00
Alex Orlenko c0c6a33f94 Add new option sort_keys to DeserializeOptions (Lua::from_value method)
Closes #303
2023-08-07 11:44:52 +01:00
Alex Orlenko 0cb0a345dd Hide clippy warning converting i32 to i32 2023-08-07 11:44:35 +01:00
Alex Orlenko 3e479be4e5 Cosmetic changes for the Value conversions + add tests 2023-08-06 23:41:27 +01:00
Alex Orlenko 170aa53e29 Change Table::raw_len() output type to usize. 2023-08-06 22:45:06 +01:00
Akase Cho 021ee946fc Add helper functions to Value (#299)
Add helper functions to `Value`
2023-08-06 22:44:32 +01:00
Alex Orlenko 0b928fdfee Faster table ops 2023-08-03 10:37:52 +01:00
Alex Orlenko e858384cd4 Add table get/set benchmark 2023-08-03 10:37:39 +01:00
Alex Orlenko c062cddd87 Fastpath IntoLua/FromLua for StdString and &str
This includes direct push to Lua stack and getting value from Lua stack.
2023-08-03 10:02:20 +01:00
Alex Orlenko 94a79656ad Faster Function::call() 2023-08-03 01:16:52 +01:00
Alex Orlenko cd0c8a4584 Optimize async functionality:
Rewrite using the new `push_into_stack()`/`from_stack()` methods.
Also store thread state (pointer) in `Thread` struct to avoid getting it every time.
Async userdata methods still need to have arguments stored in ref thread as stack is empty on every poll().
2023-08-03 00:56:17 +01:00
Alex Orlenko 4fff14a144 impl Drop for MultiValue
This action would automatically return container to the pool on drop (instead of doing it manually)
2023-08-01 11:21:26 +01:00
Alex Orlenko 196c09a0d6 New (unsafe and private) methods for IntoLua/FromLua traits: push_into_stack/from_stack.
They allow to push Value directly to Lua stack or get Value from Lua stack without creating MultiValue container.
This approach is a big optimization opportunity and already demonstrated great results.
For instance, obtaining `&T` for userdata methods are now work directly from stack value without copying to auxiliary stack.
2023-07-31 22:13:23 +01:00
Alex Orlenko 114f072269 Update module entrypoint function.
Return c_int instead of Result (and avoid potential panic on Rust side).
2023-07-31 17:49:07 +01:00
Alex Orlenko b3211f13ee Minor fixes in v0.9 release notes 2023-07-31 10:17:40 +01:00
Alex Orlenko 1f1463c482 v0.9.0-rc.3 2023-07-28 22:07:26 +01:00
Alex Orlenko bec40ee5ea mlua-sys: v0.3.1 2023-07-28 22:04:00 +01:00
Alex Orlenko dc94d51d97 Update Luau compiler options 2023-07-28 21:45:19 +01:00
Alex Orlenko 3a096ae64a Fix warning when compiling chunk tests 2023-07-28 21:45:13 +01:00
Alex Orlenko a85e757d4d Bump luau-src to v0.6.0+luau588
This release has better codegen support (and breaking changed unfortunately)
2023-07-28 20:30:46 +01:00
Alex Orlenko 312886846c Fix link to v0.9 release notes in README 2023-07-28 11:14:58 +01:00
Alex Orlenko cdc1c8415e v0.9.0-rc.2 2023-07-28 00:24:37 +01:00
Alex Orlenko f2142731fd mlua-sys: v0.3.0 2023-07-28 00:23:49 +01:00
Alex Orlenko 1dd62c5ffc mlua_derive: v0.9.0-rc.2 2023-07-28 00:23:04 +01:00
Alex Orlenko 95d8b2fd0f Update v0.9 release notes 2023-07-28 00:22:28 +01:00
Alex Orlenko 51a2959312 Add v0.9 release notes
Prepare v0.9-rc.2 release
2023-07-28 00:07:31 +01:00
Alex Orlenko 28bcc73f2c Move impl IntoLua for WrappedFunction to the function.rs file 2023-07-27 15:19:34 +01:00
Alex Orlenko 8b71f94141 Fix AsChunk trait to support capturing wrapped Lua types (AnyUserData::wrap() and Function::wrap()).
This is a breaking change, the `'lua` lifetime used to in environment moved to the trait declaration.
2023-07-27 13:56:48 +01:00
Alex Orlenko 3bda1351c3 Fix lua51/52 case in userdata tests 2023-07-26 23:18:30 +01:00
Alex Orlenko 4daa631178 Set __type metatable field for Luau instead of __name.
Other Lua versions continue to use `__name` field.
Also make `MetaMethod` enum non-exhaustive (and add `Type` variant)
Closes #295
2023-07-26 21:43:26 +01:00
Alex Orlenko 9f5325ef2f Use c-unwind ABI (Rust 1.71+) 2023-07-22 23:54:30 +01:00
Alex Orlenko 6fb65a8fbe Fix loading luau code starting with \t 2023-07-22 18:44:44 +01:00
Alex Orlenko f6dff65d9b Do not rely on vendored feature when testing luajit memory limit 2023-07-21 23:25:28 +01:00
Alex Orlenko 4fa9aa1e5b Pin lua-src and luajit-src versions 2023-07-21 23:22:47 +01:00
Alex Orlenko e1c075fa51 Use giraffate/clippy-action 2023-07-21 10:34:29 +01:00
Alex Orlenko df9251fb52 Support LUA_LIB_NAME in module mode for windows.
In this mode we will link with the particular lua library.
2023-07-21 10:34:26 +01:00
Alex Orlenko ead6be4a52 Make module and vendored features mutually exclusive 2023-07-20 23:23:03 +01:00
Alex Orlenko 4f48c7e5dc Update README 2023-07-20 21:58:01 +01:00
Alex Orlenko fac39a2f46 Drop openresty specific luajit extensions 2023-07-20 01:02:23 +01:00
Alex Orlenko 483e6be207 Support vendored module mode for windows (raw-dylib linking)
This is requires Rust 1.71+
2023-07-20 00:43:57 +01:00
Alex Orlenko 0e030d21b0 Add #[derive(FromLua)] macro to opt-in into FromLua<T> where T: 'static + Clone (userdata type).
Future macro implementations will allow making T from Lua tables/other values.
Relates to #291.
2023-07-16 01:57:28 +01:00
Alex Orlenko 355a0606c3 v0.9.0-rc.1 2023-07-11 22:15:31 +01:00
Alex Orlenko 2277ee4860 Rename UserDataRegistrar to LuaUserDataRegistry 2023-07-11 22:15:15 +01:00
Alex Orlenko 8a4977e8e7 Use pretty format in the repl example 2023-07-11 21:39:05 +01:00
Alex Orlenko 128c357e07 Use __tostring if __name is not available when pretty printing userdata values 2023-07-11 21:36:02 +01:00
Alex Orlenko 9f0fc27c52 Make Lua::push_value() and Lua::pop_value() public (but hidden from the docs).
Can be useful for low-level intergration with mlua values.
Also closes #215.
2023-07-11 20:03:41 +01:00
Alex Orlenko bfd1c29c0a Add back Table::raw_sequence_values() with deprecation notice 2023-07-11 16:02:28 +01:00
Alex Orlenko 5fca2db6d3 Revert Add error-send feature 2023-07-11 15:50:15 +01:00
Alex Orlenko 8ecbf5b76a v0.9.0-rc.1 2023-07-11 01:09:45 +01:00
Alex Orlenko d037c31b4d mlua-sys: v0.2.1 2023-07-11 01:07:37 +01:00
Alex Orlenko 4fe89c9d45 mlua_derive: v0.9.0-rc.1 2023-07-11 01:07:07 +01:00
Alex Orlenko 389526bb80 Some doc improvements 2023-07-11 00:28:24 +01:00
Alex Orlenko 4adc3116f9 Add error-send feature 2023-07-11 00:20:52 +01:00
Alex Orlenko 8e0bdc9934 Remove clippy::let_and_return ignore 2023-07-10 22:58:15 +01:00
Alex Orlenko 7dc6e4c132 Add Error::runtime() helper 2023-07-10 22:41:03 +01:00
Alex Orlenko 44b8c8b7a6 Add AnyUserData::wrap() to more easy way of creating _any_ userdata in Lua.
This is similar to `Function::wrap()`.
2023-07-10 16:36:38 +01:00
Alex Orlenko 6b8b79266f Drop futures-timer dev-dependency 2023-07-10 01:16:27 +01:00
Alex Orlenko 08ab685d8d Update itertools dev-dependency 2023-07-10 01:16:20 +01:00
Alex Orlenko 057deb0169 Update rustyline dependency to 12.0 2023-07-10 01:05:40 +01:00
Alex Orlenko 44533d2c9d Add new module attribule skip_memory_check to improve performance
in module mode (by skipping memory allocations check) with extra risks.
2023-07-10 00:43:19 +01:00
Alex Orlenko 54c7a2d191 Update compile tests 2023-07-10 00:10:20 +01:00
Alex Orlenko c38a1f060b Various cosmetic changes 2023-07-10 00:04:40 +01:00
Alex Orlenko 5dca743b0c Improve performance AnyUserData::{is_serializable/inspect/serialize} 2023-07-10 00:01:26 +01:00
Alex Orlenko 01c1952c9f Rename AnyUserData::get_*_user_value to AnyUserData::*_user_value.
To be more consistent with function like `Lua::named_registry_value`, `Lua::add_data_ref` and os on.
2023-07-09 23:50:44 +01:00
Alex Orlenko dbc3dd95d4 Fix Luau vector4 formatting when throwing it in exception 2023-07-09 23:14:49 +01:00
Alex Orlenko a9b0cdfc03 Take &str as function name in TableExt and AnyUserDataExt traits 2023-07-09 22:49:16 +01:00
Alex Orlenko 1c20494158 Use usize instead of c_int for table capacity (Lua::create_table_with_capacity) 2023-07-09 22:11:26 +01:00
Alex Orlenko c9294ad642 Add #[must_use] hint and const to some Luau Compiler functions 2023-07-09 14:13:06 +01:00
Alex Orlenko 3a71bfb8a0 Refactor Lua 5.4 warnings to use &str instead of CStr 2023-07-09 14:13:05 +01:00
Alex Orlenko 24e14c4874 Always set hook on a current Lua context (state) and remove MainThreadNotAvailable as no longer needed 2023-07-09 12:33:21 +01:00
Alex Orlenko 20826a69ae Re-export ffi crate (mlua-sys) 2023-07-09 11:31:59 +01:00
Alex Orlenko 5127903c38 Remove generic type parameter from init_userdata_metatable 2023-07-08 17:00:46 +01:00
Alex Orlenko 541139b944 Change Lua ref types Debug print from Ref{index} to Ref{pointer}
The index is always uniq per instance, but pointer can exactly tell when
several values reference to the same Lua internal value.
2023-07-08 12:52:14 +01:00
Alex Orlenko 925a2816cc clippy 2023-07-06 00:59:46 +01:00
Alex Orlenko b3b8d79446 Make Debug interface more user friendly
- use String instead of Vec<u8>
- update docs
- unify fields between lua5.x/luau
- line numbers are `usize`
2023-07-06 00:38:33 +01:00
Alex Orlenko 85f17a269d Add Table:is_empty() function 2023-06-26 10:50:18 +01:00
Alex Orlenko b169031d4e Don't use any metamethods in Table::sequence_values() iterator.
This is matches with `Table::pair()` iterator.
Remove `Table::raw_sequence_values()` iterator.
2023-06-26 10:46:59 +01:00
Alex Orlenko 399e469328 Update "async userdata method" benchmark 2023-06-21 22:23:43 +01:00
Alex Orlenko 1367a033d7 Don't clone function when doing call_async() 2023-06-21 13:14:17 +01:00
Alex Orlenko c1168d3ec1 Refactor call_async() functions to use static dispatch outside of traits 2023-06-21 12:44:24 +01:00
Alex Orlenko b05698d55b impl UserData for Rc<T> and Arc<T> where T: UserData 2023-06-21 01:30:09 +01:00
Alex Orlenko aeacf6cacc Remove allow(dead_code) from mlua-sys build scripts 2023-06-20 23:49:48 +01:00
Alex Orlenko 1f0e81c9a1 Add a dedicated type for Luau vector.
Refactor existing implementation and add 4-dimensional vectors support.
2023-06-20 13:30:42 +01:00
Alex Orlenko c2bfc9ec52 Implement PartialEq<[T]> for tables 2023-06-19 23:28:08 +01:00
Alex Orlenko 9fdba541e9 Update UserDataMethods::add_async_method() functions to take &T as second argument instead of cloning T.
New functions: `UserDataMethods::add_async_method_mut()`, `UserDataMethods::add_async_meta_method_mut()`.
2023-06-15 00:34:41 +01:00
Alex Orlenko cf0524aa23 Use lua_iscfunction instead of lua_getinfo in Function::environment() 2023-06-08 09:33:48 +01:00
Alex Orlenko 03b6dfb2c3 v0.9.0-beta.3 2023-06-07 14:40:49 +01:00
Alex Orlenko cf1cb31150 mlua_derive: v0.9.0-beta.2 2023-06-07 13:08:02 +01:00
Alex Orlenko 7d586f52f2 mlua-sys: v0.2.0 2023-06-07 13:06:51 +01:00
Alex Orlenko e85818e199 mlua-sys: include luau to luau-codegen feature flag 2023-06-07 13:05:16 +01:00
Alex Orlenko 16bec29274 Fix setting Luau version number in _VERSION env var 2023-06-07 12:35:45 +01:00
Alex Orlenko 5a135a331a Add UserDataFields::add_field() method to add static fields to UserData.
Plus `UserDataFields::add_meta_field()` for static meta fields.
Fix propagating fields to wrapped UserData types.
2023-06-07 12:18:24 +01:00
Alex Orlenko e7b712e29f Rely on LUA_MINSTACK in callbacks 2023-06-06 21:38:45 +01:00
Alex Orlenko d5483988d2 Optimize polling async functions (40% performance win) 2023-06-04 20:53:12 +01:00
Alex Orlenko 0f5c68dcf8 Update criterion to 0.5 2023-06-04 18:31:10 +01:00
Alex Orlenko 8ab0ccf11c Don't keep poll function in environment globals when polling async functions.
This is redundant after deprecating scoped async.
Closes #281
2023-06-04 02:31:29 +01:00
Alex Orlenko 9596f2e9ee Fix warning when compiling for lua51 2023-06-03 14:24:34 +01:00
Alex Orlenko 1dc32452e6 Support getting and setting environment for Lua functions.
Closes #218
2023-06-03 12:56:49 +01:00
Alex Orlenko 9785722d61 Require environment to be a Table instead of Value in Chunks.
In addition to that, `AsChunk::env()` renamed to `AsChunk::environment()`.
2023-06-03 00:00:53 +01:00
Alex Orlenko c905a34b1d (Luau) Set thread globals to main thread globals when resetting the thread 2023-06-02 21:13:23 +01:00
Alex Orlenko a1089dbf95 Optimize a bit calling Rust functions from Lua.
This is done by reusing upvalue as a captured variable.
Also in async functions upvalue is always set (async cannot be scoped) so extra checks removed.
2023-05-30 15:15:20 +01:00
Alex Orlenko baf25e263f Change _mlua to __mlua prefix for internal chunk names 2023-05-30 00:36:09 +01:00
Alex Orlenko 3abf73dee5 Update FunctionInfo doc and interface 2023-05-30 00:33:57 +01:00
Alex Orlenko 4adebd31f9 Update .gitignore 2023-05-30 00:33:16 +01:00
Alex Orlenko cea2d7fd15 Refactor application data container.
Now it's allowed at the same time mutably and immutably borrow different types.
Each value in the application data container is stored in it's own `RefCell` wrapper.
Also added new function `Lua::try_set_app_data()`.
2023-05-29 00:30:31 +01:00
Alex Orlenko e0224ab159 Use futures-core and futures-tasks via futures-util.
Just to reduce number of dependencies.
2023-05-29 00:30:29 +01:00
Alex Orlenko 6dee339783 Use lua_closethread in AsyncThread::drop() 2023-05-28 01:43:15 +01:00
Alex Orlenko b674d7906d Include (known) userdata type to Value debug pretty-print 2023-05-28 01:12:42 +01:00
Alex Orlenko 3d7796de55 clippy 2023-05-27 22:08:42 +01:00
Alex Orlenko 4306e6e978 Add Value::to_string() method similar to luaL_tolstring.
It uses `__tostring` metamethod if set.
Closes #279
2023-05-27 01:29:08 +01:00
Alex Orlenko 68e65a8ffe Set __name field in userdata metatable if not provided 2023-05-25 10:59:46 +01:00
Alex Orlenko 2efc637ab9 Move util into mod and add short_type_name function 2023-05-25 10:23:52 +01:00
Alex Orlenko 22e748557c Add new feature flag luau-jit to enable experimental Luau codegen backend 2023-05-24 23:13:12 +01:00
Alex Orlenko 77effb5055 Refactor callback_error_ext
Also it makes rust-analyzer happy
2023-05-24 00:46:59 +01:00
Alex Orlenko 1c66a02878 Update to Lua 5.4.6 2023-05-21 00:55:06 +01:00
Alex Orlenko 1ac98e7d16 - Allow downcasting error wrapped into Error::WithContext
- Overwrite error context when called multiple times
2023-05-08 23:39:53 +01:00
Alex Orlenko bbd2fe06e1 Use DeserializeOwned and remove lifetime from LuaSerdeExt trait 2023-05-06 23:07:34 +01:00
Alex Orlenko d951cb503f Add Value::NULL constant 2023-05-06 22:53:40 +01:00
Alex Orlenko bbd2488f79 Add OwnedAnyUserData::take() 2023-05-03 23:29:33 +01:00
Alex Orlenko 3253ae8f4a v0.9.0-beta.2 2023-04-27 23:13:32 +01:00
Alex Orlenko fdda0d3724 Update compile tests 2023-04-27 23:06:38 +01:00
Alex Orlenko a1d385c7b7 Add OwnedString 2023-04-26 23:17:27 +01:00
Alex Orlenko 21b834decc Update coverage calculation (exclude mlua-sys) 2023-04-26 22:22:11 +01:00
Alex Orlenko 4daa7de997 Various improvements for owned types, including:
- tests
- shortcuts for `OwnedFunction` and `OwnedAnyUserData`
2023-04-26 15:40:48 +01:00
Alex Orlenko bd68a155c0 Replace ManuallyDrop with MaybeUninit 2023-04-26 15:40:47 +01:00
Alex Orlenko 993aaf44c7 mlua-sys: v0.1.1 2023-04-24 11:25:22 +01:00
Alex Orlenko 54cbc62727 mlua-sys: use luau-src v0.5.6+luau573 2023-04-24 11:24:32 +01:00
Alex Orlenko 2d6a0fdf9c Check for reference leak detection only in mlua integratin tests.
This is not necessary an error and should not be enforced by default.
Fixes #268.
2023-04-23 10:56:26 +01:00
Alex Orlenko d0cbd32ad2 (clippy) mlua_derive: don't clone function name 2023-04-22 11:07:33 +01:00
Alex Orlenko be64706cff Missing doc for OwnedAnyUserData 2023-04-14 10:29:34 +01:00
Alex Orlenko c178bc0a55 cargo fmt 2023-04-14 10:29:12 +01:00
Alex Orlenko 0fccdfed5c Fix feature flags for owned types 2023-04-14 01:17:26 +01:00
Alex Orlenko aaf0a5e44a Remove usage of crate::ffi (clippy) 2023-04-14 00:36:31 +01:00
Alex Orlenko 98888883bc mlua-sys: clippy 2023-04-14 00:31:06 +01:00
Alex Orlenko 65e72f39ae Remove unstable feature flag from Function::wrap/wrap_mut/wrap_async 2023-04-14 00:21:18 +01:00
Alex Orlenko f9d1213c4b Don't set html_root_url (it's not recommended) 2023-04-13 22:44:40 +01:00
Alex Orlenko dc10c80e61 Add documentation url to mlua-sys 2023-04-13 22:32:27 +01:00
Alex Orlenko 23b9cefdca Add missing doc_cfg feature to mlua-sys 2023-04-13 21:55:37 +01:00
Alex Orlenko 15dc0e9f23 Move ffi module into mlua-sys crate 2023-04-12 23:23:34 +01:00
Alex Orlenko 0c53e09e30 Update tarpaulin settings 2023-04-12 01:03:33 +01:00
Alex Orlenko cdbf04f50c Add pretty-print to the Debug formatting to Value to Table.
This would allow dumping any Lua variable in human readable form.
2023-04-11 20:36:41 +01:00
Alex Orlenko ba324b4f54 Allow deserializing Lua null into unit(()) or unit struct. See #264 2023-04-10 01:49:51 +01:00
Alex Orlenko 3e83753466 Add Thread::set_hook() function 2023-04-08 23:53:48 +01:00
Alex Orlenko 288934c82c Refactor HookTriggers (make it const) 2023-04-05 01:02:27 +01:00
Alex Orlenko 483bc80fc4 Add must_use to AsyncThread 2023-04-05 00:23:13 +01:00
Alex Orlenko 354c69acc4 Update README 2023-03-30 23:22:01 +01:00
Alex Orlenko 6fa0a78ec0 Prepare v0.9.0-beta.1 2023-03-30 23:18:10 +01:00
Alex Orlenko f66932e8d2 Revert "Rename Owned*::to_ref() to as_ref()"
This reverts commit 5394faca16.
2023-03-30 22:35:43 +01:00
Alex Orlenko da6d5a93c9 Update rustyline dependency 2023-03-30 22:34:41 +01:00
Alex Orlenko 1be927bc5d Don't call error handler for memory errors in coroutines 2023-03-30 17:29:57 +01:00
Alex Orlenko 0848ddcdf7 Clippy 2023-03-28 20:53:22 +01:00
Alex Orlenko e182d474e0 More user-friendly error message about missing value for name attribute in module macro
Update the `[lua_module]` doc
2023-03-28 16:27:16 +01:00
Alex Orlenko 0f937b0a03 Switch to syn 2.0 2023-03-28 13:56:59 +01:00
Alex Orlenko cd9fc1d08f Update Error doc 2023-03-26 20:58:43 +01:00
Alex Orlenko d9aac08b81 Support setting memory limit for Lua 5.1/JIT/Luau
Other versions already support this feature.
Closes #119
2023-03-26 00:06:52 +00:00
Alex Orlenko 9c1669020b Clippy 2023-03-25 23:38:57 +00:00
Alex Orlenko 742307a267 Add &Lua to luau interrupt callback (fixes #197) 2023-03-25 16:30:31 +00:00
Alex Orlenko 781ded573a Seal LuaSerdeExt/TableExt/AnyUserDataExt 2023-03-19 02:38:21 +00:00
Alex Orlenko 6a647f58be Add Error::downcast_ref() method 2023-03-19 00:22:51 +00:00
Alex Orlenko 4bc6501d2e Tests for the ErrorContext trait 2023-03-17 01:31:07 +00:00
Alex Orlenko 8d80321738 Add ErrorContext extension trait to attach additional context to Error 2023-03-17 01:20:57 +00:00
Alex Orlenko a0d37fd182 Rename error to cause in Error::BadArgument 2023-03-16 23:48:54 +00:00
Alex Orlenko 03787668fd Improve error reporting when calling Rust functions from Lua.
In particular new error type `Error::BadArgument` added to help identify bad argument position or name (eg `self` for userdata).
2023-03-14 23:23:46 +00:00
Alex Orlenko 3059f82552 Update README and CHANGELOG 2023-03-12 23:54:19 +00:00
Alex Orlenko 22fbcfd9fb Update edition everywhere 2023-03-12 01:03:31 +00:00
Alex Orlenko 304880bfd1 Fast metatable check for Lua::push_userdata_ref 2023-03-10 10:34:41 +00:00
Alex Orlenko 33c276d0b4 Optimize userdata methods call when __index and fields_getters are nil 2023-03-04 11:58:34 +00:00
Alex Orlenko 87d027e8ac Rename rust_module in tests to test_module
This causes warning during compilation
2023-03-03 18:58:13 +00:00
Alex Orlenko d7db1b7f2e Add Waker lifetime to WakerGuard 2023-03-03 18:29:04 +00:00
Alex Orlenko 22c973af02 Async: Don't clone function when executing it from recycled thread 2023-03-03 18:22:39 +00:00
Alex Orlenko 8c18fa1764 Async: store pointer to Waker in extra data rather than in Lua ref thread 2023-03-03 15:19:54 +00:00
Alex Orlenko 5394faca16 Rename Owned*::to_ref() to as_ref() 2023-03-02 15:36:11 +00:00
Alex Orlenko 4a3f6d60ad Update exports in libs and prelude 2023-03-02 15:34:42 +00:00
Alex Orlenko 68b60e2a0a Add UserDataRef and UserDataRefMut types that implement FromLua
and can be used as accessors to underlying `AnyUserData` type.
2023-02-26 21:52:28 +00:00
Alex Orlenko 888bd77e60 Add AnyUserDataExt to prelude 2023-02-25 23:59:29 +00:00
Alex Orlenko 949906f9f7 Fix potential deadlock when trying to reuse dropped RegistryKey.
If no free registry id found, we call protect_lua! macro while keeping mutex guard to the unref list.
Protected calls can trigger garbage collection and if RegistryKey is placed in userdata being collected, this can lead to deadlock.
The solution is drop mutex guard as soon as possible.
Also this commit includes optimization in creating reference in Lua registry.
2023-02-22 20:15:40 +00:00
Alex Orlenko 94f01e597c Add AnyUserDataExt trait with auxiliary functions for AnyUserData 2023-02-18 23:40:28 +00:00
Alex Orlenko b8e3290f35 Update LuaRef/LuaOwnedRef
Move content of `Lua::make_owned_ref` into `LuaRef::into_owned`
Add crate-visible `Lua::clone` function (not trait)
2023-02-15 09:20:03 +00:00
Alex Orlenko 03ab828342 Return evicted value to Lua::set_app_data if it's already present.
Add "Panics" section to `Lua::*_app_data` docs.
2023-02-12 23:38:09 +00:00
Alex Orlenko b66bff9155 Drop Lua::async_scope as it's unsound 2023-02-12 22:25:10 +00:00
Alex Orlenko f52abf919e Add create_userdata_ref/create_userdata_ref_mut for scope (#206)
New methods would allow creating userdata objects from (mutable) reference
to a UserData of registered type.
2023-02-12 16:37:02 +00:00
Alex Orlenko b790b525c1 Fix clippy warnings 2023-02-07 22:43:48 +00:00
Alex Orlenko f5182e0584 Force protected mode for long enough strings 2023-02-06 23:46:31 +00:00
Alex Orlenko 47c8300ccf Allow registering and creating custom userdata types that don't necessary implement the UserData trait.
This is useful to register 3rd party types that cannot implement `UserData` due to Rust orphan rules.
See #206
2023-02-03 23:46:04 +00:00
Alex Orlenko 8339621f9c Rename to_lua_err -> into_lua_err 2023-01-06 21:35:15 +00:00
Alex Orlenko 16cbb442e5 Fix clippy warnings 2023-01-06 20:50:29 +00:00
Alex Orlenko 88da28a68d Allow deserializing values from serializable UserData using Lua::from_value() method.
Closes #240
2023-01-06 10:41:17 +00:00
Alex Orlenko a62061f453 Update README 2023-01-04 20:58:03 +00:00
Alex Orlenko cb3d58b1c7 Better Debug for String 2023-01-04 15:56:57 +00:00
Alex Orlenko fa46720f5a Add Table::clear() method 2023-01-02 15:50:21 +00:00
Alex Orlenko 72b9209ae9 Rename wrapped_failure/thread cache to pool 2022-12-30 21:48:12 +00:00
Alex Orlenko 0cb7058476 Rename MultiValue cache to pool 2022-12-30 14:50:39 +00:00
Alex Orlenko ac8b3424d1 Update criterion 2022-12-30 13:45:59 +00:00
Alex Orlenko 2cb4b49d0d Don't use unstable features in examples 2022-12-30 13:39:27 +00:00
Alex Orlenko d67332a3ac Refactor userdata::MetaMethod.
Take `impl AsRef<str>` as name of metamethod (instead of `Into<MetaMethod>`) in UserDataMethods/UserDataFields.
Change position of generic args in userdata.
2022-12-24 19:16:58 +00:00
Alex Orlenko 22c6e9a7e7 Enable unstable feature flag in CI 2022-12-22 16:30:14 +00:00
Alex Orlenko 1d4a135e8e Add Function::wrap/Function::wrap_mut/Function::wrap_async to wrap functions into a type that implements IntoLua trait.
This is useful to avoid calling `lua.create_function*` every time when `Function` handle is needed.
2022-12-22 16:24:35 +00:00
Alex Orlenko 9d28b790e7 Fix module tests 2022-12-21 21:05:23 +00:00
Alex Orlenko 61c285746c Remove FromLua impl for UserData+Clone 2022-12-20 23:30:18 +00:00
Alex Orlenko a13c188de3 Rename ToLua/ToLuaMulti -> IntoLua/IntoLuaMulti 2022-12-19 23:00:47 +00:00
Alex Orlenko c60f633a62 Add "unstable" feature flag.
Hide owned types under the new feature flag.
Drop OwnedString/OwnedThread types (unlikely they are useful).
2022-12-19 22:26:44 +00:00
Alex Orlenko 56abc4a700 Refactor AsChunk trait 2022-12-19 21:12:36 +00:00
Alex Orlenko 9b4e3a1598 Refactor LuaInner state
Add static_assertions to check for auto traits impl on compilation stage
Bump MSRV to 1.63 (required for `Ref::filter_map`)
2022-12-19 16:09:05 +00:00
Alex Orlenko 0aa30226df Check for invalid args when parsing #[lua_module(...)] proc macro 2022-12-18 00:35:41 +00:00
Alex Orlenko fdb5724053 Add ?Sized to PartialEq for Lua String 2022-12-18 00:35:41 +00:00
rise0chen b532f86674 feat: Support custom name of lua_module 2022-12-18 00:35:41 +00:00
Alex Orlenko c44463f030 Replace impl AsRef<str> with simple &str 2022-12-18 00:35:41 +00:00
Alex Orlenko de38e299fc Use impl AsRef<str> for userdata methods/fields instead of generic param.
Use `impl AsRef<str>` for module names and named registry values.
2022-12-18 00:35:41 +00:00
Alex Orlenko 7526773a90 Do not clear usevalues when taking value out of userdata.
It has big performance penalty.
Lua GC can collect uservalues when userdata is not referenced anymore.
2022-12-18 00:35:41 +00:00
Alex Orlenko bf79d9e75d Initial implementation of owned Lua types 2022-12-18 00:35:41 +00:00
Joel Natividad 02c08c6350 upgrade bstr from 0.2 to 1.0 2022-12-18 00:35:40 +00:00
Alex Orlenko 05eb20f9c6 Fix subtraction overflow when calculating index for MultiValue::get().
Return `None` instead of panic in debug mode.
Fixes #232
2022-12-15 21:10:31 +00:00
Alex Orlenko 9716918517 Fix lifetime of DebugNames in Debug::names() and DebugSource in Debug::source().
This can cause use-after-free if used wrong.
Now invalid code would not compile.
Reported in #230
2022-12-09 23:55:38 +00:00
Alex Orlenko c88417a3b6 Redefine _VERSION for Luau to include version number.
https://github.com/khvzak/mlua/discussions/228
2022-12-03 21:21:28 +00:00
Alex Orlenko 6e95386f30 Update Luau to 0.555 (luau-src 0.5.0)
This version includes inreased LUAI_MAXCSTACK limit (100000)
2022-12-03 17:55:26 +00:00
Alex Orlenko 6807dfa22e v0.8.6 2022-11-07 01:05:14 +00:00
Alex Orlenko f27c49f931 Fix bug when recycled Registry slot can be set to Nil.
This can result in allocating the same slot twice and rewriting old value.
Lua uses (registry) table length to find next free slot and having Nil in the middle of the table can impact length calculation.
With this fix we ensure that Nil values uses a special LUA_REFNIL slot.
2022-11-07 00:10:57 +00:00
Alex Orlenko 693a808b6e v0.8.5 2022-10-30 12:31:26 +00:00
Alex Orlenko a6ca65aa74 Better checks and tests when trying to modify a Luau readonly table 2022-10-30 11:41:09 +00:00
Alex Orlenko a7278cab78 Fix Table::raw_push for luau when readonly 2022-10-29 23:02:19 +01:00
Alex Orlenko e1bbd00a33 Fix getting caller information from Lua::load 2022-10-28 10:43:05 +01:00
Alex Orlenko e98998d6ac Add push/pop methods to Table 2022-10-23 23:27:17 +01:00
Alex Orlenko cca177df5b Minor serde optimizations 2022-10-23 03:13:24 +01:00
Alex Orlenko 65396a910f Optimize Lua::create_table to use reference thread if possible 2022-10-23 00:24:04 +01:00
Alex Orlenko de69d10d73 Optimize Lua::create_string to use reference thread if possible 2022-10-23 00:14:13 +01:00
Alex Orlenko 959e61b97c Optimize tables:
- Use reference thread directly for simple cases
- Fix issue when calling raw_set on readonly table (Luau)
- Add fasttrack methods for get/set/len when metatable is not set
2022-10-22 23:45:58 +01:00
Alex Orlenko 1040c0a347 Fix typo 2022-10-22 23:44:21 +01:00
Alex Orlenko 9045419586 Strings optimizations: get access to bytes directly from ref thread. 2022-10-22 23:43:08 +01:00
Alex Orlenko fcd162f3eb Replace Lua::ref_thread_exec 2022-10-22 23:39:49 +01:00
Alex Orlenko 0354703dbf Update formatting (Lua registry values) 2022-10-22 23:34:19 +01:00
Alex Orlenko 9831d0e397 Check that allocation does not exceed isize::MAX
See https://github.com/rust-lang/rust/issues/101899
2022-10-22 23:13:02 +01:00
Alex Orlenko 7f14d93c2b v0.8.4 2022-10-09 14:40:15 +01:00
Alex Orlenko 29c6c9cb58 Fix clippy warnings 2022-10-09 14:08:33 +01:00
Alex Orlenko e01af22bac Update push_userdata_ref 2022-10-09 13:56:30 +01:00
Alex Orlenko cfa959e599 Don't allocate extra byte for userdata (luau) 2022-10-09 13:56:06 +01:00
Alex Orlenko e523fb2c86 Update to Luau 0.548 (luau-src >=0.4) 2022-10-08 23:34:47 +01:00
Alex Orlenko 81a9998559 Update CI 2022-09-13 00:09:03 +01:00
Alex Orlenko 050ac4f5ad Add luaL_setfuncs to preserved functions 2022-09-05 23:13:06 +01:00
Alex Orlenko 967cbb53b0 Update compile tests 2022-08-23 23:12:37 +01:00
Alex Orlenko 44a8d86d8a Fix error message in tests (luau) 2022-08-23 23:08:00 +01:00
Alex Orlenko 33278d4a64 Check if chunk is empty in luaL_loadbufferx for Luau (fixes #200) 2022-08-23 11:35:08 +01:00
Trisha 20a16839aa Fix typo in doc comment 2022-08-23 01:47:17 +01:00
Alex Orlenko 83ed442bf9 v0.8.3 2022-08-02 12:43:15 +01:00
Alex Orlenko bf6708ba58 Fallback to Lua internal allocator if unable to create Lua VM with Rust one.
This should fix #194
2022-08-02 10:35:39 +01:00
Alex Orlenko 0cd724f63b Fix Lua assertion when inspecting another thread stack.
The thread can be dead and it's not safe to call __tostring metamethod (if present) on error object.
Fixes #195
2022-08-01 22:07:39 +01:00
Alex Orlenko 5330b900fd Check that Lua state is non-null in init_from_ptr() 2022-08-01 15:21:14 +01:00
Alex Orlenko ee32dc33f3 Check that lua_newstate() returns non-null Lua state 2022-08-01 12:56:36 +01:00
Alex Orlenko ab029b087d Close to-be-closed variables for Lua 5.4 when using call_async functions
Fixes #192
2022-07-31 15:33:02 +01:00
Alex Orlenko 185fee956d v0.8.2 2022-07-25 14:23:25 +01:00
Alex Orlenko 4fc69be5f6 Update rustyline dev dependency 2022-07-25 14:14:01 +01:00
Alex Orlenko 3ec076693a Add FAQ 2022-07-24 11:59:04 +01:00
Alex Orlenko 95adf8df8e Add __iter to destructed metatable (luau) 2022-07-24 11:55:36 +01:00
Alex Orlenko 4a25eab257 Merge pull request #189 from hack3ric/master
`Function::bind`: simply clone the function if args are empty `MultiValue`
2022-07-22 00:28:18 +01:00
Alex Orlenko 40fe937878 Fix a bug in Function::bind when args and binds are empty
This leads to a Lua assertion due to using wrong stack index
2022-07-22 00:24:53 +01:00
Eric Long a6b178328d Merge branch 'master' of https://github.com/hack3ric/mlua 2022-07-21 20:55:28 +08:00
Eric Long f3f173fcb6 Function::bind: simply clone the function if args are empty MultiValue 2022-07-21 20:54:49 +08:00
Alex Orlenko d3b48cf2f3 Use Luau tags to mark userdata objects as destructed 2022-07-18 10:38:22 +01:00
Alex Orlenko f9ff6116db Use MaybeUninit instead of hack in protect_lua_closure 2022-07-17 11:33:51 +01:00
Alex Orlenko f75af6d75f Fix clippy warnings 2022-07-17 11:11:30 +01:00
Alex Orlenko 059e41bafb Optimize WrappedFailure userdata detection.
This is done by comparing a metatable pointer to previously saved one (it never changes).
2022-07-17 11:00:46 +01:00
Alex Orlenko f7ee6dc635 Add lua_xpush to 5.1-5.4 2022-07-17 11:00:46 +01:00
Alex Orlenko 0919ff21c9 Merge pull request #181 from hack3ric/master
Add `MultiValue::get` & add clear error in `Index`
2022-07-01 10:10:16 +01:00
Eric Long 553251761f Add MultiValue::get & add clear error in Index 2022-07-01 01:59:40 +08:00
Alex Orlenko b46b476f80 v0.8.1 2022-06-29 15:41:22 +01:00
Alex Orlenko 70e16b51ae Set source name to full file path in Luau require 2022-06-29 11:25:16 +01:00
Alex Orlenko 9596b97faa Update Lua::create_userdata doc about sharing metatable.
Closes #175
2022-06-29 00:25:26 +01:00
Alex Orlenko 8cbb3d8fae Merge pull request #180 from khvzak/userdata_proxy
Add Lua::create_proxy for easy access to UserData static fields and f…
2022-06-29 00:05:59 +01:00
Alex Orlenko e7f494530f Add Lua::create_proxy for easy access to UserData static fields and functions
Closes #178
2022-06-28 23:03:29 +01:00
Alex Orlenko 3746c3614f Merge pull request #179 from hack3ric/master
Implement utilities for MultiValue
2022-06-28 22:56:28 +01:00
Alex Orlenko 9af1aaf889 Make clippy happy 2022-06-28 21:28:48 +01:00
Eric Long c20eb20a59 Implement utilities for MultiValue 2022-06-29 01:21:32 +08:00
Alex Orlenko 04ba93137c Add Table::to_pointer() and String::to_pointer() functions 2022-06-27 14:58:48 +01:00
Alex Orlenko 113f91ace3 Derive Default for Lua::MemoryInfo 2022-06-27 13:57:15 +01:00
Alex Orlenko ff0d923aae Don't use custom allocator for non-vendored LuaJIT (fixes #176) 2022-06-27 13:04:32 +01:00
Alex Orlenko d9ac7abff2 v0.8.0 2022-06-08 23:57:35 +01:00
Alex Orlenko 48aa97351d Add Sandboxing section to README 2022-06-08 23:56:26 +01:00
Alex Orlenko 4516ca0bb5 Forgotten part of userdata performance optimization for Lua 5.1 2022-06-07 17:59:22 +01:00
Alex Orlenko 9005f32a98 Add ffi::luau::lua_tolightuserdata introduced in Luau 0.528 2022-06-06 22:40:29 +01:00
Alex Orlenko 93d36b9068 More performance optimization (userdata part) 2022-06-06 21:42:55 +01:00
Alex Orlenko f9f32bffce Update bundled luau/jit versions 2022-06-06 15:59:28 +01:00
Alex Orlenko da5010e6de Switch to 2021 edition and bump MSRV to 1.56+ 2022-06-06 15:40:29 +01:00
Alex Orlenko 4afca8f5bb Prepare for v0.8.0 release 2022-06-02 22:44:07 +01:00
Alex Orlenko b065db37c2 Always use Rust allocator for Lua 5.1/u 2022-05-31 02:09:58 +01:00
Alex Orlenko a100ebb3d7 Fix luaL_requiref glb handling for Lua<=5.1 2022-05-30 21:45:25 +01:00
Alex Orlenko a05a58b258 Update integer/number coercion logic 2022-05-30 19:26:12 +01:00
Alex Orlenko 3766cee4dd Experimental optimization to skip oom check 2022-05-30 18:42:34 +01:00
Alex Orlenko 99becdb590 More inline attributes 2022-05-29 20:17:09 +01:00
Alex Orlenko 799b4e9f0d Make Variadic::new and MultiValue::new const 2022-05-28 21:13:49 +01:00
Alex Orlenko 93f0b2a5de Update type of lua_Integer for lua51/52 2022-05-27 12:26:56 +01:00
Alex Orlenko 62db3adde1 Add "send" test 2022-05-26 22:22:23 +01:00
Alex Orlenko afa343ff08 Add [track_caller] attribute when working with app data container 2022-05-26 20:16:09 +01:00
Alex Orlenko 4f06f614a9 v0.8.0-beta.5 2022-05-25 01:47:38 +01:00
Alex Orlenko 30ba616a8a Correctly deserialize newtype struct (#168) 2022-05-24 23:26:17 +01:00
Alex Orlenko 0076aa735a Add Function::coverage for Luau to obtain coverage report 2022-05-24 02:01:46 +01:00
Alex Orlenko bcf2cbea37 Add Value::to_pointer() function.
Closes #165 and #166.
2022-05-18 13:15:08 +01:00
Alex Orlenko 8cd594c609 Fix typo 2022-05-18 12:15:39 +01:00
Alex Orlenko 8d71ea79aa Cache bytecode for internal Lua chunks 2022-05-16 20:29:02 +01:00
Alex Orlenko 2a8c5c7f82 Refactor Function::bind implementation.
Make it possible to bind async function arguments.
Fixes #161
2022-05-15 01:15:31 +01:00
Alex Orlenko 6b2ceb60c4 Fix Send impl 2022-05-12 19:24:12 +01:00
Alex Orlenko 3aac7131ee Add Iter metamethod for Luau (available from 0.526) 2022-05-09 14:34:10 +01:00
Alex Orlenko 1807fa789c Don't require Lua sources for modules in build system 2022-05-07 22:16:54 +01:00
Alex Orlenko 7db7526a08 Update to Luau 0.526 2022-05-07 02:08:50 +01:00
Alex Orlenko bea9b0f8b5 Assert stack for Luau in callback_error_ext when operating on a cached wrapped failure.
Should solve #153.
2022-04-27 00:20:25 +01:00
Alex Orlenko 32a3f478bd v0.8.0-beta.4 2022-04-25 23:18:27 +01:00
Alex Orlenko 294b3b7aae Bump mlua_derive to 0.8.0-beta.1 2022-04-19 23:55:16 +01:00
Alex Orlenko b0dabe9d3c Add parking_lot feature to README 2022-04-19 23:50:11 +01:00
Alex Orlenko 5133a9837a Add Function::info() to get information about functions.
Closes #149 and #7.
2022-04-18 18:28:11 +01:00
Alex Orlenko 790df77965 Fix "unused" warning in tests 2022-04-17 22:39:21 +01:00
Alex Orlenko a62636fde0 More parking_lot ci tests 2022-04-17 17:29:51 +01:00
Alex Orlenko d77c0e3b8d Merge pull request #147 from sondr3/parking_lot
Add parking_lot dependency and feature
2022-04-17 01:38:03 +01:00
Alex Orlenko 3904213ed0 Faster lua_rotate for Lua < 5.3 2022-04-17 01:02:40 +01:00
Sondre Aasemoen a5ce0c1409 Add parking_lot dependency and feature 2022-04-16 19:45:21 +02:00
Alex Orlenko dba76f3994 Merge pull request #145 from zh-jq/patch-v0.7.4
support luajit version 2.0.4
2022-04-15 20:25:32 +01:00
Alex Orlenko 86f506a170 Fix clippy warnings 2022-04-14 21:55:36 +01:00
Alex Orlenko d3975bdf30 Refactor AsChunk trait.
Remove blanket implementation for T: AsRef<[u8]>
Implement for `std::path::Path`
2022-04-14 20:48:00 +01:00
Alex Orlenko 21affdadfd Remove Luau compiler options from Chunk in favour of setting Compiler instance.
Add ability to set global Luau compiler used for load all chunks including via require function.
2022-04-14 00:54:29 +01:00
Alex Orlenko 17473269f8 Update collectgarbage for Luau: support more options 2022-04-13 20:50:50 +01:00
Alex Orlenko 88e3e92009 Define CallbackUpvalue/AsyncCallbackUpvalue as type alises to Upvalue 2022-04-13 18:29:51 +01:00
Alex Orlenko 1ba7a409d5 Update compile tests 2022-04-13 14:30:52 +01:00
Alex Orlenko ef81f40afa Add tests for static Lua instance 2022-04-13 14:30:45 +01:00
Alex Orlenko a018d3b6dc Revert "Remove Lua::into_static/from_static"
This reverts commit f75b7b7879.
2022-04-13 13:44:59 +01:00
Alex Orlenko 0215c31a3a Refactor Lua instance structure.
The idea is to keep same Lua instance across all calls and only change context inside callbacks.
This should solve #104.
2022-04-13 13:44:12 +01:00
Alex Orlenko 5cd82d0f6b Refactor main_state handling 2022-04-13 13:44:11 +01:00
zhangjingqiang 75697e3a3b support luajit version 2.0.4 2022-04-13 13:36:45 +08:00
Alex Orlenko d1c80be033 Don't cast *const to *mut (for pointers comparison) 2022-04-08 22:52:55 +01:00
Alex Orlenko a8fef51e86 (minor) Fix capturing variables in protect_lua closures 2022-04-08 21:15:57 +01:00
Alex Orlenko 28a063c1e5 Implement Hash for Lua String 2022-04-08 20:02:18 +01:00
Alex Orlenko 55fac90a74 Fix Luau documentation in docs.rs
Enable `Lua::gc_inc` for Luau
Mark `debug` module as safe for Luau
2022-04-08 20:02:12 +01:00
Alex Orlenko 70d287cf9f Don't pass Lua handler to interrupt callback (Luau) as it's not safe.
Optimize callback_error_ext to check stack only before allocating a new WrappedFailure.
2022-04-08 10:45:28 +01:00
Alex Orlenko d607039a31 v0.8.0-beta.3 2022-04-03 23:38:41 +01:00
Alex Orlenko 0ea65a2985 Fix doc test 2022-04-03 22:13:54 +01:00
Alex Orlenko 8c333354d3 Update Luau to 0.521 2022-03-31 23:28:37 +01:00
Alex Orlenko f63f147265 Add set_mutable_globals to Luau compiler 2022-03-31 20:35:52 +01:00
Alex Orlenko 595dc3e95f Move some Luau functionality to a new module
Immplement native "vector" function to construct vectors
2022-03-31 19:31:37 +01:00
Alex Orlenko ac28c8d8d2 Add vector_lib/vector_ctor options to Luau Compiler (hidden) 2022-03-31 19:05:19 +01:00
Alex Orlenko d5315da8d1 Fix tests 2022-03-31 12:23:21 +01:00
Alex Orlenko a7cc7f328a Increase minimum lua-src version to 544 2022-03-31 00:09:52 +01:00
Alex Orlenko 516f01ed44 Increase internal caches size 2022-03-31 00:07:05 +01:00
Alex Orlenko 4492a20bbc Make LuaHook as Fn instead of FnMut to remove Mutex and improve performance 2022-03-30 23:55:34 +01:00
Alex Orlenko 595bc3a2b3 Support Luau interrupts (closes #138) 2022-03-30 22:01:06 +01:00
Alex Orlenko 87c10ca93d Sandboxing support 2022-03-28 23:42:35 +01:00
Alex Orlenko f75b7b7879 Remove Lua::into_static/from_static 2022-03-26 00:30:57 +00:00
Alex Orlenko de1cfa070f v0.8.0-beta.2 2022-03-25 00:44:02 +00:00
Alex Orlenko ec1fa04085 Update docs 2022-03-25 00:43:54 +00:00
Alex Orlenko 714dd6249f Enable Thread::reset for Luau 2022-03-23 21:13:48 +00:00
Alex Orlenko 5089dd73c0 Update luau-src to 0.2.1 to fix performance issues related to longjmp 2022-03-23 01:25:34 +00:00
Alex Orlenko 9533f08d3a Use lua_xpush for Luau 2022-03-23 01:24:54 +00:00
Alex Orlenko 0a3b65af88 Support readonly table attribute (luau) 2022-03-22 21:33:29 +00:00
Alex Orlenko 3a9c8c2da2 Add Luau vector datatype support 2022-03-22 21:14:06 +00:00
Alex Orlenko 952dae1de5 v0.8.0-beta.1 2022-03-22 01:01:18 +00:00
Alex Orlenko c88e5f0c4b Update README 2022-03-22 00:49:50 +00:00
Alex Orlenko 55b778c68b Fix clippy warnings 2022-03-22 00:47:31 +00:00
Alex Orlenko c6d3727171 Prepare v0.8.0-beta.1 2022-03-21 01:37:08 +00:00
Alex Orlenko 4e0ba6559e Add require function to Luau 2022-03-21 01:08:48 +00:00
Alex Orlenko 405cff5d49 Move StaticUserData* implementation from lua module to the new userdata_impl 2022-03-21 01:08:48 +00:00
Alex Orlenko 37cddc6b1c Replace compat53_countlevels with lua_stackdepth for Luau 2022-03-21 01:08:48 +00:00
Alex Orlenko 2c069f7033 Update to luau 0.519 2022-03-21 01:08:48 +00:00
Alex Orlenko 1e61d1dadc Return binary chunks support in safe mode.
Lua has many ways to load binary bytecode and this restriction was easy to bypass.
2022-03-21 01:08:47 +00:00
Alex Orlenko 32124b31a0 Move chunks structs to a new module.
Add Luau Compiler interface to compile sources.
2022-03-21 01:08:47 +00:00
Alex Orlenko 3e5f8e7bb8 Enable Lua::inspect_stack for Luau 2022-03-21 01:08:47 +00:00
Alex Orlenko cab92f4ea2 Update luaL_traceback for Luau 2022-03-21 01:08:47 +00:00
Alex Orlenko fa99f62a99 Fix __index and __newindex wrappers for Luau 2022-03-21 01:08:40 +00:00
Alex Orlenko eed7b1f3af More ffi module refactoring 2022-03-20 20:30:20 +00:00
Alex Orlenko c322e028e2 Initial Luau support 2022-03-20 20:30:20 +00:00
Alex Orlenko 6dc127f4eb Refactor ffi module
Initial Luau support work
2022-03-20 20:30:18 +00:00
Tarik02 d4f8dce597 Fix async userdata __index, __newindex metamethods 2022-03-20 20:03:47 +00:00
Alex Orlenko c85616137a v0.7.4 2022-03-01 19:59:49 +00:00
Alex Orlenko f52d106a82 Fix "find_dummy" compile error if no lua feature selected 2022-03-01 19:59:42 +00:00
Alex Orlenko 10826a7e67 Update create_registry_value doc 2022-02-15 19:45:37 +00:00
Alex Orlenko 18c3255c90 Optimize Lua::create_registry_value by reusing previously expired registry keys. 2022-02-14 21:20:57 +00:00
Alex Orlenko 6190427f37 Add Lua::replace_registry_value 2022-02-14 20:51:24 +00:00
Alex Orlenko 9a5a341e44 Recognize LuaJIT TCDATA type to generate correct panic message.
Relates to #127
Should be fixed in a next major release by adding support of TCDATA type.
2022-02-12 18:40:18 +00:00
Alex Orlenko dd91ebfbe5 Grow/check Lua stack in unpack 2022-02-12 17:10:43 +00:00
Alex Orlenko f9fe869b76 Optimize async calls:
Rewrite "unpack" function using C api rather than high level abstraction.
2022-01-29 12:39:30 +00:00
Alex Orlenko 6e4033abba Fix tests for Lua 5.4.4 2022-01-29 12:36:09 +00:00
Alex Orlenko c213a85ef0 v0.7.3 2022-01-19 18:12:45 +00:00
Alex Orlenko 4963cc1b8b Fix cross compilation (bug introduced in 84a174c) 2022-01-19 17:42:32 +00:00
Alex Orlenko 418e8fba32 v0.7.2 2022-01-17 11:12:46 +00:00
Alex Orlenko 08a7e30820 Update README 2022-01-17 11:11:56 +00:00
Alex Orlenko 19676bda40 Update CHANGELOG 2022-01-16 23:50:26 +00:00
Alex Orlenko 5a06778fbc Always restore original Lua state after creating Future in async call.
Fixes #121
2022-01-16 20:57:43 +00:00
Alex Orlenko e33bdddc7a Pass Box wrapped pointer to allocator fn instead of reference 2022-01-08 23:06:01 +00:00
Alex Orlenko cfb5d3fd45 Fix clippy warnings 2021-12-28 12:23:06 +00:00
Alex Orlenko 84a174c94d Allow pkg-config to omit include paths if they equals to standard.
See #114
2021-12-28 12:02:02 +00:00
Alex Orlenko 888b2bbf8d Refactor build/find_normal.rs to include error messages instead of unwrap() 2021-12-28 10:26:12 +00:00
Alex Orlenko 386eb7a2b7 v0.7.1 2021-12-23 19:42:52 +00:00
Alex Orlenko 66c9c633b2 Generate traceback from level 0 (instead of 1)
Fixes #112
2021-12-23 17:53:20 +00:00
Alex Orlenko f1e357ce52 Hide (doc) into_static/from_static methods 2021-12-01 00:28:08 +00:00
Alex Orlenko be09a1fabf Update lifetimes for few functions in Lua 2021-11-30 23:14:08 +00:00
Alex Orlenko 30af045c6f Lift minimum Rust requirement to 1.53+ 2021-11-29 10:46:09 +00:00
Alex Orlenko e809640b7e Update CI (switch to ubuntu-20.04 and add luajit52) 2021-11-27 13:44:30 +00:00
Alex Orlenko cb3ef20ffc Update README (remove ref to benchmarks) 2021-11-27 13:44:09 +00:00
Alex Orlenko 3b6564c02a Update compile tests (messages) 2021-11-27 13:42:22 +00:00
Alex Orlenko 89580dd863 v0.7.0 2021-11-25 18:18:38 +00:00
Alex Orlenko a0554a56d4 Update dependencies 2021-11-25 18:12:29 +00:00
Alex Orlenko 2fee3e7891 Update docs 2021-11-25 18:01:41 +00:00
Alex Orlenko 9f073ad879 Update README & CHANGELOG 2021-11-25 13:32:13 +00:00
Alex Orlenko 1b74c89509 More performance optimizations 2021-11-25 11:16:12 +00:00
Alex Orlenko 440862de38 Update tests 2021-11-23 19:12:27 +00:00
Alex Orlenko 066d28f5e5 Update Lua::unload doc & fix clippy warnings 2021-11-23 19:01:23 +00:00
Alex Orlenko 4528f93345 Merge pull request #103 from polachok/unload
Add `Lua::unload()` to unload modules
2021-11-23 18:11:13 +00:00
Alexander Polakov d49757c7db Add unload() to remove module 2021-11-23 18:36:57 +03:00
Alex Orlenko 170818c469 Add call() function to TableExt to call tables with __call metamethod as functions 2021-11-21 23:47:45 +00:00
Alex Orlenko 55c8af1e6b Add minimum Rust requirements to README 2021-11-21 23:42:49 +00:00
Alex Orlenko e2ebe65306 Add get_named_user_value and set_named_user_value to AnyUserData 2021-11-21 23:42:47 +00:00
Alex Orlenko f0f5a8a0af Update CHANGELOG 2021-11-17 23:41:30 +00:00
Alex Orlenko 0e1be19cbb Move hook_proc under Lua::set_hook to use callback_error_ext 2021-11-16 12:27:56 +00:00
Alex Orlenko 0ef709672d Add set_warning_function/remove_warning_function/warning functions to Lua for 5.4
This utilizes Lua 5.4 warnings system (https://www.lua.org/manual/5.4/manual.html#pdf-warn)
2021-11-16 12:05:34 +00:00
Alex Orlenko 41503b4fb8 Update callback_error_ext (+ fix callback multi states handling) 2021-11-16 11:53:51 +00:00
Alex Orlenko 19bd254e1e Update comments 2021-11-14 23:27:20 +00:00
Alex Orlenko 50f20e0c2c Add thread (coroutine) cache to reset and later reuse to execute async functions.
It works on Lua 5.4 and LuaJIT (vendored) with `lua_resetthread` function.
2021-11-14 23:19:47 +00:00
Alex Orlenko 7efe807199 Include StdLib to prelude 2021-11-14 23:19:47 +00:00
Alex Orlenko 63ea8c7662 Rename wrapped_failures pool to cache 2021-11-14 23:19:47 +00:00
Alex Orlenko 863d36d5a1 Performance optimization: cache and reuse MultiValue containers 2021-11-12 15:32:53 +00:00
Alex Orlenko a8017c10b9 Update callback_error(_ext) 2021-11-12 12:41:03 +00:00
Alex Orlenko 2c7d7117d2 Optimize MultiValue allocations (recycle old container) 2021-11-12 10:55:20 +00:00
Alex Orlenko 6d689c35aa Update hook::Debug struct 2021-11-09 18:20:36 +00:00
Alex Orlenko 8af1304fd0 Add Lua::inspect_stack to get information about the interpreter runtime stack.
This functionality is provided by `lua_getstack`.
2021-11-09 14:18:14 +00:00
Alex Orlenko 153502ec73 Add set_nth_user_value and get_nth_user_value to AnyUserData
with `n` up to 65535 for all Lua versions.
2021-11-08 21:16:31 +00:00
Alex Orlenko 2ea2b1f4fb Refactor Error::CallbackError reporting and include source to
fmt::Display implementation.
This fixes #71.
2021-11-07 22:53:37 +00:00
Alex Orlenko ef8c1556e6 Add optional Send to Lua::app_data 2021-11-07 15:03:17 +00:00
Alex Orlenko b0da2fc439 Switch Table::serialize to FxHashSet 2021-11-07 14:49:19 +00:00
Alex Orlenko 204eedde3c Merge branch 'dev' 2021-11-07 13:07:22 +00:00
Alex Orlenko fbc2973aff Fix recursive tables serialization when using serde::ser::Serialize
implementation for Table.
Fixes #98.
2021-11-06 21:12:00 +00:00
Alex Orlenko d0641d812f Refactor a bit conversion int->number 2021-11-04 13:15:26 +00:00
Alex Orlenko ad70ba54a5 Publish AnyUserData::take 2021-11-04 12:32:19 +00:00
Alex Orlenko 806f0bcef4 Add luajit52 support (LuaJIT with partial compatibility with Lua 5.2) 2021-11-04 12:26:11 +00:00
Alex Orlenko 0741db7565 Make (De)SerializeOptions as const 2021-11-04 01:07:38 +00:00
Alex Orlenko d88a4282c7 Replace macro-based implementation ToLua for arrays to const generics 2021-11-04 00:59:39 +00:00
Alex Orlenko d7d987fa14 Add async meta methods for all Lua except 51 2021-11-04 00:57:49 +00:00
Alex Orlenko 4d3ac6d8c5 Add new "application data" api 2021-10-19 11:45:39 +01:00
Alex Orlenko a9ca99349c Switch to FxHash 2021-10-19 11:45:38 +01:00
Alex Orlenko f71db80a74 Change definition of lua_State to opaque struct 2021-10-19 11:45:38 +01:00
Alex Orlenko 09af3e021a v0.6.6 2021-10-12 12:22:14 +01:00
Alex Orlenko 9e3d495f91 Calculate coverage for Lua 5.4 instead of Lua 5.3 2021-10-12 12:10:04 +01:00
Alex Orlenko cab2e5a48e Fix clippy warnings 2021-10-12 12:02:42 +01:00
Alex Orlenko 25a4879cde Update examples: async_http_client/async_http_server/async_tcp_server
Make them following best practices and remove `unsafe` code.
2021-10-12 11:55:25 +01:00
Alex Orlenko ed48b11e7f Update documentation references
Using rustdoc links (see RFC https://github.com/rust-lang/rfcs/pull/1946)
2021-10-12 00:49:45 +01:00
Alex Orlenko 559f9e6c6b Add target.aarch64-apple-darwin conf to module examples 2021-10-11 18:07:34 +01:00
Alex Orlenko 458b06796c Fixed LUA_REGISTRYINDEX when generating glue.rs on cross-compiling for lua51(jit) 2021-10-08 21:15:22 +01:00
Alex Orlenko 259eb09ae1 v0.6.5 2021-10-05 18:11:32 +01:00
Alex Orlenko a544e41b33 Add (hidden) method UserData::take() to take out value from userdata 2021-10-05 15:46:50 +01:00
Alex Orlenko 235fba821e Update CHANGELOG 2021-10-04 23:28:24 +01:00
Alex Orlenko c8c64a1b5a Add serializing i128/u128 types.
Fixes #81.
2021-10-04 23:20:11 +01:00
Alex Orlenko eff0bbb052 Add Location::caller() information to Lua::load() if chunk's name is None 2021-10-03 23:20:07 +01:00
Alex Orlenko d098c9ccf6 Refactor Waker handling in async code.
Instead of storing `Option<Waker>` in the Lua registry, store it on the reference thread.
It gives approx +10% performance gain when calling async function.
2021-10-03 22:09:19 +01:00
Alex Orlenko c62b17a5c8 Fixed bug when polling async futures (#77)
We expect first value returned via coroutine.yield() to be a special Pending type.
But instead we checked second value.
2021-10-02 07:23:24 +01:00
Alex Orlenko 584b377640 v0.6.4 2021-09-28 18:53:47 +01:00
Alex Orlenko 1141073a65 Update CHANGELOG 2021-09-28 18:51:42 +01:00
Alex Orlenko 5b1483bd56 Change syntax of protect_lua macro 2021-09-28 18:47:08 +01:00
Alex Orlenko a74b637ed4 Update check_stack requirements 2021-09-28 18:41:25 +01:00
Alex Orlenko 7623016d4a Fix doc feature labels 2021-09-28 18:15:53 +01:00
Alex Orlenko bdd3c923ba Fix table traversal used in recursion detection.
This fixes serializing same table multiple times within a parent table.
2021-09-28 16:41:39 +01:00
Alex Orlenko d586eef0f5 Refactor UserData metatables handling 2021-09-28 16:33:36 +01:00
Alex Orlenko 01154c0616 Minor refacor of wrapped failures cache 2021-09-28 16:27:24 +01:00
Alex Orlenko e42d67c70d Make protect_lua as a smart macro to choose from C/closure 2021-09-28 16:26:30 +01:00
Alex Orlenko 771a7775c5 Perf optimization: refactor metatable cache 2021-09-28 16:25:08 +01:00
Alex Orlenko ee1c8a1a3d Add inline attributes to few hot funcs 2021-09-28 16:19:29 +01:00
Alex Orlenko 3597e34ffb Fix internal HookCallback type (missing optional Send) 2021-09-28 16:19:12 +01:00
Alex Orlenko 35eedd5a5e v0.6.3 2021-09-16 00:55:49 +01:00
Alex Orlenko 53f873a482 Update compile tests error messages 2021-09-16 00:49:17 +01:00
Alex Orlenko fc1fe2c15e Add DeserializeOptions struct to control deserializer behavior.
This solves #74 and provides a way to deserialize a Lua globals table.
2021-09-15 23:45:08 +01:00
Alex Orlenko 7e7a44f4cd Update CHANGELOG 2021-08-22 16:31:14 +01:00
Alex Orlenko 63c4861520 Create FUNDING.yml 2021-08-22 16:22:20 +01:00
Alex Orlenko 27e7facf9b Fix clippy warnings 2021-08-22 00:35:31 +01:00
Alex Orlenko 31d32f2dda Wrap ExtraData to Arc<UnsafeCell>> instead of raw pointer and attach finalizer.
This would allow to properly deallocate memory in module mode when closing lua state.
2021-08-21 23:17:09 +01:00
Alex Orlenko 7d1b322e18 Change ExtraData::mem_info to Box<MemoryInfo> 2021-08-19 01:42:32 +01:00
Alex Orlenko d906405818 Simplify interface of hook::HookTriggers 2021-08-18 18:49:17 +01:00
Alex Orlenko 60fd060d47 Clarify about calling Lua::init_from_ptr() multiple times 2021-08-17 15:34:34 +01:00
Alex Orlenko 9f02a9ca09 Add Debug::event() to the hook's Debug structure 2021-08-17 15:17:03 +01:00
Alex Orlenko 1d7f105585 Don't catch Rust panics in userdata finalizer on drop 2021-08-06 11:14:16 +01:00
Alex Orlenko 1020315a9b Update documentation about FromLua for UserData. Closes #64 2021-08-04 12:12:02 +01:00
Alex Orlenko 628b70f561 v0.6.2 2021-07-13 20:29:25 +01:00
Alex Orlenko 8a7e546c66 Shrink unsafe block in Lua::load_from_function and update doc 2021-07-13 16:43:00 +01:00
Alex Orlenko 97bd288f56 Add CI tests with enabled address sanitizer
Don't use release mode in tests
Try to use cache in CI
2021-07-13 16:17:29 +01:00
Alex Orlenko 59253410bb Update trybuild messages 2021-07-13 11:59:33 +01:00
Alex Orlenko 5fb7b96704 Replace custom UserDataRef(Mut) with standard Ref(Mut) 2021-07-13 10:53:10 +01:00
Alex Orlenko 17162b1e0b UserData improvements:
1) Optimize `make_userdata` call
2) Refactor `UserDataWrapped` and make it enum
2021-07-11 01:30:17 +01:00
Alex Orlenko 934aa3b65f Forgotten optimization for box_method_mut 2021-07-09 23:25:48 +01:00
Alex Orlenko 3bffe1d7c3 Combine WrappedError and WrappedPanic structs to WrappedFailure enum 2021-07-09 00:05:29 +01:00
Alex Orlenko 582b2c585f Rename init_gc_metatable_for and get_gc_metatable_for 2021-07-08 22:57:54 +01:00
Alex Orlenko 56b6b128b5 Minor fixes/compilation after revert 2021-07-08 22:53:53 +01:00
Alex Orlenko 1731f5d61b Revert "Make protect_lua as a smart macro to choose from C/closure"
This reverts commit 84fe5f7f76.
2021-07-08 18:41:10 +01:00
Alex Orlenko 60822d12d2 Revert "Move away from metatable hashmap cache to direct keys"
This reverts commit adbc9ccc9b.
2021-07-08 18:40:14 +01:00
Alex Orlenko 8ff610529b Don't wrap ExtraData to Arc<Mutex> and use raw pointer instead.
This causes serious performance issues and given that Lua is
single threaded (not Sync) it's safe to use a raw pointer instead.
2021-07-08 17:32:20 +01:00
Alex Orlenko adbc9ccc9b Move away from metatable hashmap cache to direct keys 2021-07-08 15:37:18 +01:00
Alex Orlenko 84fe5f7f76 Make protect_lua as a smart macro to choose from C/closure 2021-07-07 12:54:19 +01:00
Alex Orlenko 1fe583027b Add new functions: lua.load_from_function() and lua.create_c_function()
This should be useful to register embedded C modules to Lua state.
Provides a solution for #61
2021-07-05 00:03:18 +01:00
Alex Orlenko 7b5b78fa3d Add codecov.yml to don't fail on coverage change 2021-06-30 21:52:50 +01:00
Alex Orlenko 41aae83304 Optimize callback creation
Attach only one upvalue to callbacks rather than two.
This leads to less lookup to Lua registry.
2021-06-30 16:50:50 +01:00
Alex Orlenko fc84e8623e v0.6.1 2021-06-28 00:10:07 +01:00
Alex Orlenko 8aae9a7c3e Update chunk! documentation 2021-06-28 00:10:04 +01:00
Alex Orlenko 24bbd62564 Fix converting Lua sequence table to HashSet/BTreeSet 2021-06-25 01:49:09 +01:00
Alex Orlenko 30af919931 Lower once_cell dependency to 1.0 2021-06-25 00:39:10 +01:00
Alex Orlenko 89b5981c59 Fix typos 2021-06-25 00:38:49 +01:00
178 changed files with 29716 additions and 12394 deletions
+1
View File
@@ -0,0 +1 @@
github: khvzak
+6 -6
View File
@@ -6,18 +6,18 @@ jobs:
name: coverage
runs-on: ubuntu-latest
container:
image: xd009642/tarpaulin
image: xd009642/tarpaulin:develop-nightly
options: --security-opt seccomp=unconfined
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@main
- name: Generate code coverage
- name: Generate coverage report
run: |
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
cargo +nightly tarpaulin --verbose --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
- name: Upload to codecov.io
uses: codecov/codecov-action@v1
- name: Upload report to codecov.io
uses: codecov/codecov-action@v4
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: false
+206 -135
View File
@@ -7,97 +7,95 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --release --features "${{ matrix.lua }},vendored"
cargo build --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
cargo build --release --features "${{ matrix.lua }}"
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --features "${{ matrix.lua }},vendored"
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
cargo build --features "${{ matrix.lua }}"
build_aarch64_cross_macos:
name: Cross-compile to aarch64-apple-darwin
runs-on: macos-11.0
runs-on: macos-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: aarch64-apple-darwin
override: true
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: aarch64-apple-darwin
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: aarch64-unknown-linux-gnu
override: true
- name: Install ARM compiler toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
shell: bash
- name: Cross-compile
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
shell: bash
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: aarch64-unknown-linux-gnu
- name: Install ARM compiler toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
shell: bash
- name: Cross-compile
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
shell: bash
build_armv7_cross_ubuntu:
name: Cross-compile to armv7-unknown-linux-gnueabihf
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: armv7-unknown-linux-gnueabihf
override: true
- name: Install ARM compiler toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
shell: bash
- name: Cross-compile
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
shell: bash
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: armv7-unknown-linux-gnueabihf
- name: Install ARM compiler toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
shell: bash
- name: Cross-compile
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
shell: bash
test:
name: Test
@@ -105,34 +103,88 @@ jobs:
needs: build
strategy:
matrix:
os: [ubuntu-18.04, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --release --features "${{ matrix.lua }},vendored"
cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
shell: bash
- name: Run compile tests (macos lua53)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
run: |
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
shell: bash
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --features "${{ matrix.lua }},vendored"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
run: |
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
shell: bash
test_with_sanitizer:
name: Test with address sanitizer
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-latest]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
env:
RUSTFLAGS: -Z sanitizer=address
test_with_memory_limit:
name: Test with memory limit
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-latest]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with forced memory limit
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
shell: bash
env:
RUSTFLAGS: --cfg=force_memory_limit
test_modules:
name: Test modules
@@ -140,26 +192,26 @@ jobs:
needs: build
strategy:
matrix:
os: [ubuntu-18.04, macos-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit]
os: [ubuntu-latest, macos-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
shell: bash
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} module tests
run: |
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
shell: bash
test_modules_windows:
name: Test modules on Windows
@@ -172,42 +224,61 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
- uses: actions/checkout@v2
- name: Install Rust & Lua
run: |
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module && cargo test --release --features "${{ matrix.lua }}")
- uses: msys2/setup-msys2@v2
- uses: actions/checkout@main
- name: Install Rust & Lua
run: |
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
- name: Run ${{ matrix.lua }} module tests
run: |
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
test_wasm32_emscripten:
name: Test on wasm32-unknown-emscripten
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luau]
steps:
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: wasm32-unknown-emscripten
- name: Install Emscripten
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends emscripten
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --tests --features "${{ matrix.lua }},vendored"
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
rustfmt:
name: Rustfmt
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
override: true
- run: cargo fmt -- --check
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- run: cargo fmt -- --check
clippy:
name: Clippy check
runs-on: ubuntu-18.04
name: Clippy
runs-on: ubuntu-latest
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
toolchain: nightly
components: clippy
- uses: giraffate/clippy-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
reporter: 'github-pr-review'
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
+1
View File
@@ -4,3 +4,4 @@ Cargo.lock
.vscode/
.DS_Store
.stignore
+457 -4
View File
@@ -1,3 +1,456 @@
## v0.10.4 (May 5th, 2025)
- Luau updated to 0.672
- New serde option `encode_empty_tables_as_array` to serialize empty tables as arrays
- Added `WeakLua` and `Lua::weak()` to create weak references to Lua state
- Trigger abort when Luau userdata destructors are panic (Luau GC does not support it)
- Added `AnyUserData::type_id()` method to get the type id of the userdata
- Added `Chunk::name()`, `Chunk::environment()` and `Chunk::mode()` functions
- Support borrowing underlying wrapped types for `UserDataRef` and `UserDataRefMut` (under `userdata-wrappers` feature)
- Added large (52bit) integers support for Luau
- Enable `serde` for `bstr` if `serialize` feature flag is enabled
- Recursive warnings (Lua 5.4) are no longer allowed
- Implemented `IntoLua`/`FromLua` for `BorrowedString` and `BorrowedBytes`
- Implemented `IntoLua`/`FromLua` for `char`
- Enable `Thread::reset()` for all Lua versions (limited support for 5.1-5.3)
- Bugfixes and improvements
## v0.10.3 (Jan 27th, 2025)
- Set `Default` for `Value` to be `Nil`
- Allow exhaustive match on `Value` (#502)
- Add `Table::set_safeenv` method (Luau)
## v0.10.2 (Dec 1st, 2024)
- Switch proc-macro-error to proc-macro-error2 (#493)
- Do not allow Lua to run GC finalizers on ref thread (#491)
- Fix chunks loading in Luau when memory limit is enforced (#488)
- Added `String::wrap` method to wrap arbitrary `AsRef<[u8]>` into `impl IntoLua`
- Better FreeBSD/OpenBSD support (thanks to cos)
- Delay "any" userdata metatable creation until first instance is created (#482)
- Reduce amount of generated code for `UserData` (less generics)
## v0.10.1 (Nov 9th, 2024)
- Minimal Luau updated to 0.650
- Added Luau native vector library support (this can change behavior if you use `vector` function!)
- Added Lua `String::display` method
- Improved pretty-printing for Lua tables (#478)
- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
- Added `AnyUserData::destroy` method
- New `userdata-wrappers` feature to `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` (similar to v0.9)
- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
- Added `Scope::add_destructor` to attach custom destructors
- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
- Added `From<Vec>` and `Into<Vec>` support to `MultiValue` and `Variadic` types
- Bug fixes and improvements (#477 #479)
## v0.10.0 (Oct 25th, 2024)
Changes since v0.10.0-rc.1
- Added `error-send` feature flag (disabled by default) to require `Send + Sync` for `Error`
- Some performance improvements
## v0.10.0-rc.1
- `Lua::scope` is back
- Support yielding from hooks for Lua 5.3+
- Support setting metatable for Lua builtin types (number/string/function/etc)
- Added `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` traits for using in `Function::wrap`
- Added `Error::chain` method to return iterator over nested errors
- Added `Lua::exec_raw` helper to execute low-level Lua C API code
- Added `Either<L, R>` enum to combine two types into a single one
- Added a new `Buffer` type for Luau
- Added `Value::is_error` and `Value::as_error` helpers
- Added `Value::Other` variant to represent unknown Lua types (eg LuaJIT CDATA)
- Added (optional) `anyhow` feature to implement `IntoLua` for `anyhow::Error`
- Added `IntoLua`/`FromLua` for `OsString`/`OsStr` and `PathBuf`/`Path`
## v0.10.0-beta.2
- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
- `Error::CoroutineInactive` renamed to `Error::CoroutineUnresumable`.
- `IntoLua`/`IntoLuaMulti` now uses `impl trait` syntax for args (shorten from `a.get::<_, T>` to `a.get::<T>`).
- Removed undocumented `Lua::into_static`/`from_static` methods.
- Futures now require `Send` bound if `send` feature is enabled.
- Dropped lifetime from `UserDataMethods` and `UserDataFields` traits.
- `Compiler::compile()` now returns `Result` (Luau).
- Removed `Clone` requirement from `UserDataFields::add_field()`.
- `TableExt` and `AnyUserDataExt` traits were combined into `ObjectLike` trait.
- Disabled `send` feature in module mode (since we don't have exclusive access to Lua).
- `Chunk::set_environment()` takes `Table` instead of `IntoLua` type.
- Reduced the compile time contribution of `next_key_seed` and `next_value_seed`.
- Reduced the compile time contribution of `serde_userdata`.
- Performance improvements.
## v0.10.0-beta.1
- Dropped `'lua` lifetime (subtypes now store a weak reference to Lua)
- Removed (experimental) owned types (they no longer needed)
- Make Lua types truly `Send` and `Sync` (when enabling `send` feature flag)
- Removed `UserData` impl for Rc/Arc types ("any" userdata functions can be used instead)
- `Lua::replace_registry_value` takes `&mut RegistryKey`
- `Lua::scope` temporary disabled (will be re-added in the next release)
## v0.9.9
- Minimal Luau updated to 0.629
- Fixed bug when attempting to reset or resume already running coroutines (#416).
- Added `RegistryKey::id()` method to get the underlying Lua registry key id.
## v0.9.8
- Fixed serializing same table multiple times (#408)
- Use `mlua-sys` v0.6 (to support Luau 0.624+)
- Fixed cross compilation of windows dlls from unix (#394)
## v0.9.7
- Implemented `IntoLua` for `RegistryKey`
- Mark `__idiv` metamethod as available for luau
- Added `Function::deep_clone()` method (Luau)
- Added `SerializeOptions::detect_serde_json_arbitrary_precision` option
- Added `Lua::create_buffer()` method (Luau)
- Support serializing buffer type as a byte slice (Luau)
- Perf: Implemented `push_into_stack`/`from_stack` for `Option<T>`
- Added `Lua::create_ser_any_userdata()` method
## v0.9.6
- Added `to_pointer` function to `Function`/`Table`/`Thread`
- Implemented `IntoLua` for `&Value`
- Implemented `FromLua` for `RegistryKey`
- Faster (~5%) table array traversal during serialization
- Some performance improvements for bool/int types
## v0.9.5
- Minimal Luau updated to 0.609
- Luau max stack size increased to 1M (from 100K)
- Implemented `IntoLua` for refs to `String`/`Table`/`Function`/`AnyUserData`/`Thread` + `RegistryKey`
- Implemented `IntoLua` and `FromLua` for `OwnedThread`/`OwnedString`
- Fixed `FromLua` derive proc macro to cover more cases
## v0.9.4
- Fixed loading all-in-one modules under mixed states (eg. main state and coroutines)
## v0.9.3
- WebAssembly support (`wasm32-unknown-emscripten` target)
- Performance improvements (faster Lua function calls for lua51/jit/luau)
## v0.9.2
- Added binary modules support to Luau
- Added Luau package module (uses `StdLib::PACKAGE`) with loaders (follows lua5.1 interface)
- Added support of Luau 0.601+ buffer type (represented as userdata in Rust)
- LuaJIT `cdata` type is also represented as userdata in Rust (instead of panic)
- Vendored LuaJIT switched to rolling vanilla (from openresty)
- Added `Table::for_each` method for fast table pairs traversal (faster than `pairs`)
- Performance improvements around table traversal (and faster serialization)
- Bug fixes and improvements
## v0.9.1
- impl Default for Lua
- impl IntoLuaMulti for `std::result::Result<(), E>`
- Fix using wrong userdata index after processing Variadic args (#311)
## v0.9.0
Changes since v0.9.0-rc.3
- Improved non-static (scoped) userdata support
- Added `Scope::create_any_userdata()` method
- Added `Lua::set_vector_metatable()` method (`unstable` feature flag)
- Added `OwnedThread` type (`unstable` feature flag)
- Minimal Luau updated to 0.590
- Added new option `sort_keys` to `DeserializeOptions` (`Lua::from_value()` method)
- Changed `Table::raw_len()` output type to `usize`
- Helper functions for `Value` (eg: `Value::as_number()`/`Value::as_string`/etc)
- Performance improvements
## v0.9.0-rc.3
- Minimal Luau updated to 0.588
## v0.9.0-rc.2
- Added `#[derive(FromLua)]` macro to opt-in into `FromLua<T> where T: 'static + Clone` (userdata type).
- Support vendored module mode for windows (raw-dylib linking, Rust 1.71+)
- `module` and `vendored` features are now mutually exclusive
- Use `C-unwind` ABI (Rust 1.71+)
- Changed `AsChunk` trait to support capturing wrapped Lua types
## v0.9.0-rc.1
- `UserDataMethods::add_async_method()` takes `&T` instead of cloning `T`
- Implemented `PartialEq<[T]>` for tables
- Added Luau 4-dimensional vectors support (`luau-vector4` feature)
- `Table::sequence_values()` iterator no longer uses any metamethods (`Table::raw_sequence_values()` is deprecated)
- Added `Table:is_empty()` function that checks both hash and array parts
- Refactored Debug interface
- Re-exported `ffi` (`mlua-sys`) crate for easier writing of unsafe code
- Refactored Lua 5.4 warnings interface
- Take `&str` as function name in `TableExt` and `AnyUserDataExt` traits
- Added module attribule `skip_memory_check` to improve performance
- Added `AnyUserData::wrap()` to provide more easy way of creating _any_ userdata in Lua
## v0.9.0-beta.3
- Added `OwnedAnyUserData::take()`
- Switch to `DeserializeOwned`
- Overwrite error context when called multiple times
- New feature flag `luau-jit` to enable (experimental) Luau codegen backend
- Set `__name` field in userdata metatable
- Added `Value::to_string()` method similar to `luaL_tolstring`
- Lua 5.4.6
- Application data container now allows to mutably and immutably borrow different types at the same time
- Performance optimizations
- Support getting and setting environment for Lua functions.
- Added `UserDataFields::add_field()` method to add static fields to UserData
Breaking changes:
- Require environment to be a `Table` instead of `Value` in Chunks.
- `AsChunk::env()` renamed to `AsChunk::environment()`
## v0.9.0-beta.2
New features:
- Added `Thread::set_hook()` function to set hook on threads
- Added pretty print to the Debug formatting to Lua `Value` and `Table`
- ffi layer moved to `mlua-sys` crate
- Added OwnedString (unstable)
Breaking changes:
- Refactor `HookTriggers` (make it const)
## v0.9.0-beta.1
New features:
- Owned Lua types (unstable feature flag)
- New functions `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async`
- `Lua::register_userdata_type()` to register a custom userdata types (without requiring `UserData` trait)
- `Lua::create_any_userdata()`
- Added `create_userdata_ref`/`create_userdata_ref_mut` for scopes
- Added `AnyUserDataExt` trait with auxiliary functions for `AnyUserData`
- Added `UserDataRef` and `UserDataRefMut` type wrapped that implement `FromLua`
- Improved error handling:
* Improved error reporting when calling Rust functions from Lua.
* Added `Error::BadArgument` to help identify bad argument position or name
* Added `ErrorContext` extension trait to attach additional context to `Error`
Breaking changes:
- Refactored `AsChunk` trait
- `ToLua`/`ToLuaMulti` renamed to `IntoLua`/`IntoLuaMulti`
- Renamed `to_lua_err` to `into_lua_err`
- Removed `FromLua` impl for `T: UserData+Clone`
- Removed `Lua::async_scope`
- Added `&Lua` arg to Luau interrupt callback
Other:
- Better Debug for String
- Allow deserializing values from serializable UserData using `Lua::from_value()` method
- Added `Table::clear()` method
- Added `Error::downcast_ref()` method
- Support setting memory limit for Lua 5.1/JIT/Luau
- Support setting module name in `#[lua_module(name = "...")]` macro
- Minor fixes and improvements
## v0.8.10
- Update to Luau 0.590 (luau0-src to 0.7.x)
- Fix loading luau code starting with \t
- Pin lua-src and luajit-src versions
## v0.8.9
- Update minimal (vendored) Lua 5.4 to 5.4.6
- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
- Allow deserializing Lua null into unit (`()`) or unit struct.
## v0.8.8
- Fix potential deadlock when trying to reuse dropped registry keys.
- Optimize userdata methods call when __index and fields_getters are nil
## v0.8.7
- Minimum Luau updated to 0.555 (`LUAI_MAXCSTACK` limit increased to 100000)
- `_VERSION` in Luau now includes version number
- Fixed lifetime of `DebugNames` in `Debug::names()` and `DebugSource` in `Debug::source()`
- Fixed subtraction overflow when calculating index for `MultiValue::get()`
## v0.8.6
- Fixed bug when recycled Registry slot can be set to Nil
## v0.8.5
- Fixed potential unsoundness when using `Layout::from_size_align_unchecked` and Rust 1.65+
- Performance optimizations around string and table creation in standalone mode
- Added fast track path to Table `get`/`set`/`len` methods without metatable
- Added new methods `push`/`pop`/`raw_push`/`raw_pop` to Table
- Fix getting caller information from `Lua::load`
- Better checks and tests when trying to modify a Luau readonly table
## v0.8.4
- Minimal Luau updated to 0.548
## v0.8.3
- Close to-be-closed variables for Lua 5.4 when using call_async functions (#192)
- Fixed Lua assertion when inspecting another thread stack. (#195)
- Use more reliable way to create LuaJIT VM (which can fail if use Rust allocator on non-x86 platforms)
## v0.8.2
- Performance optimizations in handling UserData
- Minimal Luau updated to 0.536
- Fixed bug in `Function::bind` when passing empty binds and no arguments (#189)
## v0.8.1
- Added `Lua::create_proxy` for accessing to UserData static fields and functions without instance
- Added `Table::to_pointer()` and `String::to_pointer()` functions
- Bugfixes and improvements (#176 #179)
## v0.8.0
Changes since 0.7.4
- Luau support
- Removed C glue
- Added async support to `__index` and `__newindex` metamethods
- Added `Function::info()` to get information about functions (#149).
- Added `parking_lot` dependency under feature flag (for `UserData`)
- `Hash` implementation for Lua String
- Added `Value::to_pointer()` function
- Performance improvements
Breaking changes:
- Refactored `AsChunk` trait (added implementation for `Path` and `PathBuf`).
## v0.8.0-beta.5
- Lua sources no longer needed to build modules
- Added `__iter` metamethod for Luau
- Added `Value::to_pointer()` function
- Added `Function::coverage` for Luau to obtain coverage report
- Bugfixes and improvements (#153 #161 #168)
## v0.8.0-beta.4
- Removed `&Lua` from `Lua::set_interrupt` as it's not safe (introduced in v0.8.0-beta.3)
- Enabled `Lua::gc_inc` for Luau
- Luau `debug` module marked as safe (enabled by default)
- Implemented `Hash` for Lua String
- Support mode options in `collectgarbage` for Luau
- Added ability to set global Luau compiler (used for loading all chunks).
- Refactored `AsChunk` trait (breaking changes).
`AsChunk` now implemented for `Path` and `PathBuf` to load lua files from fs.
- Added `parking_lot` dependency and feature flag (for `UserData`)
- Added `Function::info()` to get information about functions (#149).
- Bugfixes and improvements (#104 #142)
## v0.8.0-beta.3
- Luau vector constructor
- Luau sandboxing support
- Luau interrupts (yieldable)
- More Luau compiler options (mutable globals)
- Other performance improvements
## v0.8.0-beta.2
- Luau vector datatype support
- Luau readonly table attribute
- Other Luau improvements
## v0.8.0-beta.1
- Luau support
- Refactored ffi module. C glue is no longer required
- Added async support to `__index` and `__newindex` metamethods
## v0.7.4
- Improved `Lua::create_registry_value` to reuse previously expired registry keys.
No need to call `Lua::expire_registry_values` when creating/dropping registry values.
- Added `Lua::replace_registry_value` to change value of an existing Registry Key
- Async calls optimization
## v0.7.3
- Fixed cross-compilation issue (introduced in 84a174c)
## v0.7.2
- Allow `pkg-config` to omit include paths if they equals to standard (#114).
- Various bugfixes (eg. #121)
## v0.7.1
- Fixed traceback generation for errors (#112)
- `Lua::into_static/from_static` methods have been removed from the docs and are discouraged for use
## v0.7.0
- New "application data" api to store arbitrary objects inside Lua
- New feature flag `luajit52` to build/support LuaJIT with partial compatibility with Lua 5.2
- Added async meta methods for all Lua (except 5.1)
- Added `AnyUserData::take()` to take UserData objects from Lua
- Added `set_nth_user_value`/`get_nth_user_value` to `AnyUserData` for all Lua versions
- Added `set_named_user_value`/`get_named_user_value` to `AnyUserData` for all Lua versions
- Added `Lua::inspect_stack()` to get information about the interpreter runtime stack
- Added `set_warning_function`/`remove_warning_function`/`warning` functions to `Lua` for 5.4
- Added `TableExt::call()` to call tables with `__call` metamethod as functions
- Added `Lua::unload()` to unload modules
- `ToLua` implementation for arrays changed to const generics
- Added thread (coroutine) cache for async execution (disabled by default and works for Lua 5.4/JIT)
- LuaOptions and (De)SerializeOptions marked as const
- Fixed recursive tables serialization when using `serde::Serialize` for Lua Tables
- Improved errors reporting. Now source included to `fmt::Display` implementation for `Error::CallbackError`
- Major performance improvements
## v0.6.6
- Fixed calculating `LUA_REGISTRYINDEX` when cross-compiling for lua51/jit (#82)
- Updated documentation & examples
## v0.6.5
- Fixed bug when polling async futures (#77)
- Refactor Waker handling in async code (+10% performance gain when calling async functions)
- Added `Location::caller()` information to `Lua::load()` if chunk's name is None (Rust 1.46+)
- Added serialization of i128/u128 types (serde)
## v0.6.4
- Performance optimizations
- Fixed table traversal used in recursion detection in deserializer
## v0.6.3
- Disabled catching Rust panics in userdata finalizers on drop. It also has positive performance impact.
- Added `Debug::event()` to the hook's Debug structure
- Simplified interface of `hook::HookTriggers`
- Added finalizer to `ExtraData` in module mode. This helps avoiding memory leak on closing state when Lua unloads modules and frees memory.
- Added `DeserializeOptions` struct to control deserializer behavior (`from_value_with` function).
## v0.6.2
- New functionality: `Lua::load_from_function()` and `Lua::create_c_function()`
- Many optimizations in callbacks/userdata creation and methods execution
## v0.6.1
- Update `chunk!` documentation (stable Rust limitations)
- Fixed Lua sequence table conversion to HashSet/BTreeSet
- `once_cell` dependency lowered to 1.0
## v0.6.0
Changes since 0.5.4
- New `UserDataFields` API
@@ -7,7 +460,7 @@ Changes since 0.5.4
- Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
- Added `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`.
- Added `Thread::reset()` for luajit/lua54 to recycle threads (coroutines) with attaching a new function.
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
- Improved errors reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in a module mode.
- Added `String::to_string_lossy`
- Various bugfixes and improvements
@@ -16,7 +469,7 @@ Breaking changes:
- Errors are always `Send + Sync` to be compatible with the anyhow crate.
- Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
- Removed `Result` from `Function::dump()` (never fails)
- Removed `AnyUserData::has_metamethod()` (if favour of full access to metatables)
- Removed `AnyUserData::has_metamethod()` (in favour of full access to metatables)
## v0.6.0-beta.3
@@ -31,7 +484,7 @@ Breaking changes:
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
It's possible to attach a new function to a thread (coroutine).
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
## v0.6.0-beta.1
@@ -87,7 +540,7 @@ Breaking changes:
- Lua 5.4 support with `MetaMethod::Close`.
- `lua53` feature is disabled by default. Now preferred Lua version have to be chosen explicitly.
- Provide safety guaraness for Lua state, which means that potenially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equalient for the previous `Lua::new()` function is `Lua::unsafe_new()`.
- Provide safety guarantees for Lua state, which means that potentially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equivalent to the previous `Lua::new()` function is `Lua::unsafe_new()`.
- New `send` feature to require `Send`.
- New `module` feature, that disables linking to Lua Core Libraries. Required for modules.
- Don't allow `'callback` outlive `'lua` in `Lua::create_function()` to fix [the unsoundness](tests/compile/static_callback_args.rs).
+65 -44
View File
@@ -1,79 +1,100 @@
[package]
name = "mlua"
version = "0.6.0" # remember to update html_root_url and mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
repository = "https://github.com/khvzak/mlua"
version = "0.10.4" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
rust-version = "1.79.0"
edition = "2021"
repository = "https://github.com/mlua-rs/mlua"
documentation = "https://docs.rs/mlua"
readme = "README.md"
keywords = ["lua", "luajit", "async", "futures", "scripting"]
keywords = ["lua", "luajit", "luau", "async", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT)
with async/await features and support of writing native lua modules in Rust.
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
with async/await features and support of writing native Lua modules in Rust.
"""
[package.metadata.docs.rs]
features = ["lua53", "async", "send", "serialize", "macros"]
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"examples/module",
"tests/module",
"mlua-sys",
]
[features]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
vendored = ["lua-src", "luajit-src"]
module = ["mlua_derive"]
async = ["futures-core", "futures-task", "futures-util"]
send = []
serialize = ["serde", "erased-serde"]
lua54 = ["ffi/lua54"]
lua53 = ["ffi/lua53"]
lua52 = ["ffi/lua52"]
lua51 = ["ffi/lua51"]
luajit = ["ffi/luajit"]
luajit52 = ["luajit", "ffi/luajit52"]
luau = ["ffi/luau", "dep:libloading"]
luau-jit = ["luau", "ffi/luau-codegen"]
luau-vector4 = ["luau", "ffi/luau-vector4"]
vendored = ["ffi/vendored"]
module = ["dep:mlua_derive", "ffi/module"]
async = ["dep:futures-util"]
send = ["parking_lot/send_guard", "error-send"]
error-send = []
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
macros = ["mlua_derive/macros"]
anyhow = ["dep:anyhow", "error-send"]
userdata-wrappers = []
[dependencies]
mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.7" }
mlua_derive = { version = "=0.10.1", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default-features = false }
either = "1.0"
num-traits = { version = "0.2.14" }
futures-core = { version = "0.3.5", optional = true }
futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true }
rustc-hash = "2.0"
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
serde = { version = "1.0", optional = true }
erased-serde = { version = "0.3", optional = true }
erased-serde = { version = "0.4", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", features = ["arc_lock"] }
anyhow = { version = "1.0", optional = true }
rustversion = "1.0"
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
ffi = { package = "mlua-sys", version = "0.7.0", path = "mlua-sys" }
[target.'cfg(unix)'.dependencies]
libloading = { version = "0.8", optional = true }
[dev-dependencies]
rustyline = "8.0"
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
trybuild = "1.0"
futures = "0.3.5"
hyper = { version = "0.14", features = ["client", "server"] }
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
futures-timer = "3.0"
serde_json = "1.0"
hyper = { version = "1.2", features = ["full"] }
hyper-util = { version = "0.1.3", features = ["full"] }
http-body-util = "0.1.1"
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
maplit = "1.0"
tempfile = "3"
static_assertions = "1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.5", features = ["async_tokio"] }
rustyline = "15.0"
tokio = { version = "1.0", features = ["full"] }
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(tarpaulin_include)'] }
[[bench]]
name = "benchmark"
harness = false
required-features = ["async"]
[[bench]]
name = "serde"
harness = false
required-features = ["serialize"]
[[example]]
name = "async_http_client"
required-features = ["async", "macros"]
@@ -84,11 +105,11 @@ required-features = ["async", "serialize", "macros"]
[[example]]
name = "async_http_server"
required-features = ["async", "send"]
required-features = ["async", "macros", "send"]
[[example]]
name = "async_tcp_server"
required-features = ["async", "macros"]
required-features = ["async", "macros", "send"]
[[example]]
name = "guided_tour"
+21
View File
@@ -0,0 +1,21 @@
# mlua FAQ
This file is for general questions that don't fit into the README or crate docs.
## Loading a C module fails with error `undefined symbol: lua_xxx`. How to fix?
Add the following rustflags to your [.cargo/config](http://doc.crates.io/config.html) in order to properly export Lua symbols:
```toml
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
```
## I want to add support for a Lua VM fork to mlua. Do you accept pull requests?
Adding new feature flag to support a Lua VM fork is a major step that requires huge effort to maintain it.
Regular updates, testing, checking compatibility, etc.
That's why I don't plan to support new Lua VM forks or other languages in mlua.
+76 -30
View File
@@ -1,25 +1,37 @@
# mlua
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io]
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
[github-actions]: https://github.com/khvzak/mlua/actions
[Build Status]: https://github.com/mlua-rs/mlua/workflows/CI/badge.svg
[github-actions]: https://github.com/mlua-rs/mlua/actions
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
[crates.io]: https://crates.io/crates/mlua
[API Documentation]: https://docs.rs/mlua/badge.svg
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/khvzak/mlua
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
[MSRV]: https://img.shields.io/badge/rust-1.79+-brightgreen.svg?&logo=rust
[Guided Tour](examples/guided_tour.rs)
[Guided Tour] | [Benchmarks] | [FAQ]
[Guided Tour]: examples/guided_tour.rs
[Benchmarks]: https://github.com/khvzak/script-bench-rs
[FAQ]: FAQ.md
> **Note**
>
> See v0.10 [release notes](https://github.com/mlua-rs/mlua/blob/main/docs/release_notes/v0.10.md).
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
[GitHub Actions]: https://github.com/khvzak/mlua/actions
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
[Luau]: https://luau.org
## Usage
@@ -28,25 +40,33 @@ Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` sup
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
Below is a list of the available feature flags. By default `mlua` does not enable any features.
* `lua54`: activate Lua [5.4] support
* `lua53`: activate Lua [5.3] support
* `lua52`: activate Lua [5.2] support
* `lua51`: activate Lua [5.1] support
* `luajit`: activate [LuaJIT] support
* `lua54`: enable Lua [5.4] support
* `lua53`: enable Lua [5.3] support
* `lua52`: enable Lua [5.2] support
* `lua51`: enable Lua [5.1] support
* `luajit`: enable [LuaJIT] support
* `luajit52`: enable [LuaJIT] support with partial compatibility with Lua 5.2
* `luau`: enable [Luau] support (auto vendored mode)
* `luau-jit`: enable [Luau] support with JIT backend.
* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
* `module`: enable module mode (building loadable `cdylib` library for Lua)
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
* `error-send`: make `mlua:Error: Send + Sync`
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
* `macros`: enable procedural macros (such as `chunk!`)
* `anyhow`: enable `anyhow::Error` conversion into Lua
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
[5.4]: https://www.lua.org/manual/5.4/manual.html
[5.3]: https://www.lua.org/manual/5.3/manual.html
[5.2]: https://www.lua.org/manual/5.2/manual.html
[5.1]: https://www.lua.org/manual/5.1/manual.html
[LuaJIT]: https://luajit.org/
[lua-src]: https://github.com/khvzak/lua-src-rs
[luajit-src]: https://github.com/khvzak/luajit-src-rs
[Luau]: https://github.com/luau-lang/luau
[lua-src]: https://github.com/mlua-rs/lua-src-rs
[luajit-src]: https://github.com/mlua-rs/luajit-src-rs
[tokio]: https://github.com/tokio-rs/tokio
[async-std]: https://github.com/async-rs/async-std
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
@@ -54,7 +74,9 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
### Async/await support
`mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
`mlua` supports async/await for all Lua versions including Luau.
This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
**Examples**:
- [HTTP Client](examples/async_http_client.rs)
@@ -62,6 +84,20 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
- [HTTP Server](examples/async_http_server.rs)
- [TCP Server](examples/async_tcp_server.rs)
**shell command examples**:
```shell
# async http client (hyper)
cargo run --example async_http_client --features=lua54,async,macros
# async http client (reqwest)
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
# async http server
cargo run --example async_http_server --features=lua54,async,macros,send
curl -v http://localhost:3000
```
### Serialization (serde) support
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
@@ -74,16 +110,16 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
### Compiling
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the chosen Lua version.
You have to enable one of the features: `lua54`, `lua53`, `lua52`, `lua51`, `luajit(52)` or `luau`, according to the chosen Lua version.
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
To achieve this, mlua supports `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
An example how to use them:
``` sh
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
```
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
@@ -97,7 +133,7 @@ Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = { version = "0.6", features = ["lua53", "vendored"] }
mlua = { version = "0.10", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -121,7 +157,7 @@ fn main() -> LuaResult<()> {
```
### Module mode
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.3/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10).
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
[Example](examples/module)
@@ -132,7 +168,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.6", features = ["lua53", "vendored", "module"] }
mlua = { version = "0.10", features = ["lua54", "module"] }
```
`lib.rs` :
@@ -158,11 +194,11 @@ And then (**macOS** example):
``` sh
$ cargo rustc -- -C link-arg=-undefined -C link-arg=dynamic_lookup
$ ln -s ./target/debug/libmy_module.dylib ./my_module.so
$ lua5.3 -e 'require("my_module").hello("world")'
$ lua5.4 -e 'require("my_module").hello("world")'
hello, world!
```
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config.toml` with the following content:
``` toml
[target.x86_64-apple-darwin]
rustflags = [
@@ -177,20 +213,22 @@ rustflags = [
]
```
On Linux you can build modules normally with `cargo build --release`.
Vendored and non-vendored builds are supported for these OS.
On Windows `vendored` mode for modules is not supported since you need to link to a Lua dll.
Easiest way is to use either MinGW64 (as part of [MSYS2](https://github.com/msys2/msys2) package) with `pkg-config` or
MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
On Windows the target module will be linked with `lua5x.dll` library (depending on your feature flags).
Your main application should provide this library.
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
Module builds don't require Lua lib or headers to be installed on the system.
### Publishing to luarocks.org
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
Modules written in Rust and published to luarocks:
- [`decasify`](https://github.com/alerque/decasify)
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
- [`tiktoken_core`](https://github.com/gptlang/lua-tiktoken)
- [`toml-edit`](https://github.com/vhyrro/toml-edit.lua)
- [`typst-lua`](https://github.com/rousbound/typst-lua)
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
@@ -247,6 +285,14 @@ If you encounter them, a bug report would be very welcome:
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
## Sandboxing
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in controlled environment.
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
[Luau Sandboxing]: https://luau.org/sandbox
## License
This project is licensed under the [MIT license](LICENSE)
+267 -119
View File
@@ -1,5 +1,7 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use tokio::runtime::Runtime;
use tokio::task;
@@ -10,10 +12,10 @@ fn collect_gc_twice(lua: &Lua) {
lua.gc_collect().unwrap();
}
fn create_table(c: &mut Criterion) {
fn table_create_empty(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [table empty]", |b| {
c.bench_function("table [create empty]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
@@ -24,55 +26,55 @@ fn create_table(c: &mut Criterion) {
});
}
fn create_array(c: &mut Criterion) {
fn table_create_array(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [array] 10", |b| {
c.bench_function("table [create array]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table = lua.create_table().unwrap();
for i in 1..=10 {
table.set(i, i).unwrap();
}
lua.create_sequence_from(1..=10).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn create_string_table(c: &mut Criterion) {
fn table_create_hash(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [table string] 10", |b| {
c.bench_function("table [create hash]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table = lua.create_table().unwrap();
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
let s = lua.create_string(s).unwrap();
table.set(s.clone(), s).unwrap();
}
lua.create_table_from(
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
.into_iter()
.map(|s| (s, s)),
)
.unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_lua_function(c: &mut Criterion) {
fn table_get_set(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("call Lua function [sum] 3 10", |b| {
b.iter_batched_ref(
c.bench_function("table [get and set]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function(a, b, c) return a + b + c end")
.eval::<LuaFunction>()
.unwrap()
lua.create_table().unwrap()
},
|function| {
for i in 0..10 {
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
|table| {
for (i, s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
.into_iter()
.enumerate()
{
table.raw_set(s, i).unwrap();
assert_eq!(table.raw_get::<usize>(s).unwrap(), i);
}
},
BatchSize::SmallInput,
@@ -80,168 +82,303 @@ fn call_lua_function(c: &mut Criterion) {
});
}
fn call_sum_callback(c: &mut Criterion) {
fn table_traversal_pairs(c: &mut Criterion) {
let lua = Lua::new();
let callback = lua
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
c.bench_function("table [traversal pairs]", |b| {
b.iter_batched(
|| lua.globals(),
|globals| {
for kv in globals.pairs::<String, LuaValue>() {
let (_k, _v) = kv.unwrap();
}
},
BatchSize::SmallInput,
);
});
}
fn table_traversal_for_each(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("table [traversal for_each]", |b| {
b.iter_batched(
|| lua.globals(),
|globals| globals.for_each::<String, LuaValue>(|_k, _v| Ok(())),
BatchSize::SmallInput,
);
});
}
fn table_traversal_sequence(c: &mut Criterion) {
let lua = Lua::new();
let table = lua.create_sequence_from(1..1000).unwrap();
c.bench_function("table [traversal sequence]", |b| {
b.iter_batched(
|| table.clone(),
|table| {
for v in table.sequence_values::<i32>() {
let _i = v.unwrap();
}
},
BatchSize::SmallInput,
);
});
}
fn function_create(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("function [create Rust]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
lua.create_function(|_, ()| Ok(123)).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn function_call_sum(c: &mut Criterion) {
let lua = Lua::new();
let sum = lua
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b - c))
.unwrap();
lua.globals().set("callback", callback).unwrap();
c.bench_function("call Rust callback [sum] 3 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
function.call::<_, ()>(()).unwrap();
c.bench_function("function [call Rust sum]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
fn call_async_sum_callback(c: &mut Criterion) {
fn function_call_lua_sum(c: &mut Criterion) {
let lua = Lua::new();
let callback = lua
let sum = lua
.load("function(a, b, c) return a + b - c end")
.eval::<LuaFunction>()
.unwrap();
c.bench_function("function [call Lua sum]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
fn function_call_concat(c: &mut Criterion) {
let lua = Lua::new();
let concat = lua
.create_function(|_, (a, b): (LuaString, LuaString)| Ok(format!("{}{}", a.to_str()?, b.to_str()?)))
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("function [call Rust concat string]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
i.fetch_add(1, Ordering::Relaxed)
},
|i| {
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
},
BatchSize::SmallInput,
);
});
}
fn function_call_lua_concat(c: &mut Criterion) {
let lua = Lua::new();
let concat = lua
.load("function(a, b) return a..b end")
.eval::<LuaFunction>()
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("function [call Lua concat string]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
i.fetch_add(1, Ordering::Relaxed)
},
|i| {
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
},
BatchSize::SmallInput,
);
});
}
fn function_async_call_sum(c: &mut Criterion) {
let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let sum = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
task::yield_now().await;
Ok(a + b + c)
Ok(a + b - c)
})
.unwrap();
lua.globals().set("callback", callback).unwrap();
c.bench_function("call async Rust callback [sum] 3 10", |b| {
c.bench_function("function [async call Rust sum]", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| async move {
function.call_async::<_, ()>(()).await.unwrap();
|| collect_gc_twice(&lua),
|_| async {
assert_eq!(sum.call_async::<i64>((10, 20, 30)).await.unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
fn call_concat_callback(c: &mut Criterion) {
fn registry_value_create(c: &mut Criterion) {
let lua = Lua::new();
let callback = lua
.create_function(|_, (a, b): (LuaString, LuaString)| {
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
})
.unwrap();
lua.globals().set("callback", callback).unwrap();
lua.gc_stop();
c.bench_function("call Rust callback [concat string] 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
function.call::<_, ()>(()).unwrap();
},
c.bench_function("registry value [create]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| lua.create_registry_value("hello").unwrap(),
BatchSize::SmallInput,
);
});
}
fn create_registry_values(c: &mut Criterion) {
fn registry_value_get(c: &mut Criterion) {
let lua = Lua::new();
lua.gc_stop();
c.bench_function("create [registry value] 10", |b| {
let value = lua.create_registry_value("hello").unwrap();
c.bench_function("registry value [get]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
for _ in 0..10 {
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
}
lua.expire_registry_values();
assert_eq!(lua.registry_value::<LuaString>(&value).unwrap(), "hello");
},
BatchSize::SmallInput,
);
});
}
fn create_userdata(c: &mut Criterion) {
struct UserData(i64);
fn userdata_create(c: &mut Criterion) {
struct UserData(#[allow(unused)] i64);
impl LuaUserData for UserData {}
let lua = Lua::new();
c.bench_function("create [table userdata] 10", |b| {
c.bench_function("userdata [create]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table: LuaTable = lua.create_table().unwrap();
for i in 1..11 {
table.set(i, UserData(i)).unwrap();
}
lua.create_userdata(UserData(123)).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_userdata_method(c: &mut Criterion) {
struct UserData(i64);
fn userdata_call_index(c: &mut Criterion) {
struct UserData(#[allow(unused)] i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("method", |_, this, ()| Ok(this.0));
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
}
}
let lua = Lua::new();
lua.globals().set("userdata", UserData(10)).unwrap();
let ud = lua.create_userdata(UserData(123)).unwrap();
let index = lua
.load("function(ud) return ud.test end")
.eval::<LuaFunction>()
.unwrap();
c.bench_function("call [userdata method] 10", |b| {
b.iter_batched_ref(
c.bench_function("userdata [call index]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(index.call::<LuaString>(&ud).unwrap(), "test");
},
BatchSize::SmallInput,
);
});
}
fn userdata_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
}
}
let lua = Lua::new();
let ud = lua.create_userdata(UserData(123)).unwrap();
let method = lua
.load("function(ud, i) return ud:add(i) end")
.eval::<LuaFunction>()
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("userdata [call method]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do userdata:method() end end")
.eval::<LuaFunction>()
.unwrap()
i.fetch_add(1, Ordering::Relaxed)
},
|function| {
function.call::<_, ()>(()).unwrap();
|i| {
assert_eq!(method.call::<usize>((&ud, i)).unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
});
}
fn call_async_userdata_method(c: &mut Criterion) {
#[derive(Clone, Copy)]
fn userdata_async_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("add", |_, this, i: i64| async move {
task::yield_now().await;
Ok(this.0 + i)
});
}
}
let lua = Lua::new();
lua.globals().set("userdata", UserData(10)).unwrap();
let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let ud = lua.create_userdata(UserData(123)).unwrap();
let method = lua
.load("function(ud, i) return ud:add(i) end")
.eval::<LuaFunction>()
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("call async [userdata method] 10", |b| {
c.bench_function("userdata [async call method] 10", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do userdata:method() end end")
.eval::<LuaFunction>()
.unwrap()
(method.clone(), ud.clone(), i.fetch_add(1, Ordering::Relaxed))
},
|function| async move {
function.call_async::<_, ()>(()).await.unwrap();
|(method, ud, i)| async move {
assert_eq!(method.call_async::<usize>((ud, i)).await.unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
@@ -251,21 +388,32 @@ fn call_async_userdata_method(c: &mut Criterion) {
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(300)
.sample_size(500)
.measurement_time(Duration::from_secs(10))
.noise_threshold(0.02);
targets =
create_table,
create_array,
create_string_table,
call_lua_function,
call_sum_callback,
call_async_sum_callback,
call_concat_callback,
create_registry_values,
create_userdata,
call_userdata_method,
call_async_userdata_method,
table_create_empty,
table_create_array,
table_create_hash,
table_get_set,
table_traversal_pairs,
table_traversal_for_each,
table_traversal_sequence,
function_create,
function_call_sum,
function_call_lua_sum,
function_call_concat,
function_call_lua_concat,
function_async_call_sum,
registry_value_create,
registry_value_get,
userdata_create,
userdata_call_index,
userdata_call_method,
userdata_async_call_method,
}
criterion_main!(benches);
+90
View File
@@ -0,0 +1,90 @@
use std::time::Duration;
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use mlua::prelude::*;
fn collect_gc_twice(lua: &Lua) {
lua.gc_collect().unwrap();
lua.gc_collect().unwrap();
}
fn encode_json(c: &mut Criterion) {
let lua = Lua::new();
let encode = lua
.create_function(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap()))
.unwrap();
let table = lua
.load(
r#"{
name = "Clark Kent",
address = {
city = "Smallville",
state = "Kansas",
country = "USA",
},
age = 22,
parents = {"Jonathan Kent", "Martha Kent"},
superman = true,
interests = {"flying", "saving the world", "kryptonite"},
}"#,
)
.eval::<LuaTable>()
.unwrap();
c.bench_function("serialize json", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
encode.call::<LuaString>(&table).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn decode_json(c: &mut Criterion) {
let lua = Lua::new();
let decode = lua
.create_function(|lua, s: String| {
lua.to_value(&serde_json::from_str::<serde_json::Value>(&s).unwrap())
})
.unwrap();
let json = r#"{
"name": "Clark Kent",
"address": {
"city": "Smallville",
"state": "Kansas",
"country": "USA"
},
"age": 22,
"parents": ["Jonathan Kent", "Martha Kent"],
"superman": true,
"interests": ["flying", "saving the world", "kryptonite"]
}"#;
c.bench_function("deserialize json", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
decode.call::<LuaTable>(json).unwrap();
},
BatchSize::SmallInput,
);
});
}
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(500)
.measurement_time(Duration::from_secs(10))
.noise_threshold(0.02);
targets =
encode_json,
decode_json,
}
criterion_main!(benches);
-5
View File
@@ -1,5 +0,0 @@
use std::path::PathBuf;
pub fn probe_lua() -> PathBuf {
unreachable!()
}
-119
View File
@@ -1,119 +0,0 @@
use std::env;
use std::ops::Bound;
use std::path::PathBuf;
fn get_env_var(name: &str) -> String {
match env::var(name) {
Ok(val) => val,
Err(env::VarError::NotPresent) => String::new(),
Err(err) => panic!("cannot get {}: {}", name, err),
}
}
pub fn probe_lua() -> PathBuf {
let include_dir = get_env_var("LUA_INC");
let lib_dir = get_env_var("LUA_LIB");
let lua_lib = get_env_var("LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_INC");
println!("cargo:rerun-if-env-changed=LUA_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK");
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
if !include_dir.is_empty() {
if need_lua_lib {
if lib_dir.is_empty() {
panic!("LUA_LIB is not set");
}
if lua_lib.is_empty() {
panic!("LUA_LIB_NAME is not set");
}
let mut link_lib = "";
if get_env_var("LUA_LINK") == "static" {
link_lib = "static=";
};
println!("cargo:rustc-link-search=native={}", lib_dir);
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
}
return PathBuf::from(include_dir);
}
// Find using `pkg-config`
#[cfg(feature = "lua54")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.4"), Bound::Excluded("5.5")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.4");
}
lua.unwrap().include_paths[0].clone()
}
#[cfg(feature = "lua53")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.3");
}
lua.unwrap().include_paths[0].clone()
}
#[cfg(feature = "lua52")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.2");
}
lua.unwrap().include_paths[0].clone()
}
#[cfg(feature = "lua51")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.1");
}
lua.unwrap().include_paths[0].clone()
}
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.0.5"), Bound::Unbounded))
.cargo_metadata(need_lua_lib)
.probe("luajit");
lua.unwrap().include_paths[0].clone()
}
}
-243
View File
@@ -1,243 +0,0 @@
#![allow(unreachable_code)]
use std::env;
use std::fs::File;
use std::io::{Error, ErrorKind, Result, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
#[cfg_attr(
all(
feature = "vendored",
any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
),
path = "find_vendored.rs"
)]
#[cfg_attr(
all(
not(feature = "vendored"),
any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
),
path = "find_normal.rs"
)]
#[cfg_attr(
not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)),
path = "find_dummy.rs"
)]
mod find;
trait CommandExt {
fn execute(&mut self) -> Result<()>;
}
impl CommandExt for Command {
/// Execute the command and return an error if it exited with a failure status.
fn execute(&mut self) -> Result<()> {
self.status()
.and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(ErrorKind::Other, "non-zero exit code"))
}
})
.map_err(|_| {
Error::new(
ErrorKind::Other,
format!("The command {:?} did not run successfully.", self),
)
})
}
}
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut config = cc::Build::new();
config.include(include_path);
// Compile and run glue.c
let glue = build_dir.join("glue");
config
.get_compiler()
.to_command()
.arg("src/ffi/glue/glue.c")
.arg("-o")
.arg(&glue)
.execute()
.unwrap();
Command::new(glue)
.arg(build_dir.join("glue.rs"))
.execute()
.unwrap();
}
// When cross-compiling, we cannot use `build_glue` as we cannot run the generated
// executable. Instead, let's take a stab at synthesizing the likely values.
// If you're cross-compiling and using a non-vendored library then there is a chance
// that the values selected here may be incorrect, but we have no way to determine
// that here.
fn generate_glue() -> Result<()> {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut glue = File::create(build_dir.join("glue.rs"))?;
writeln!(
glue,
"/* This file was generated by build/main.rs; do not modify by hand */"
)?;
writeln!(glue, "use std::os::raw::*;")?;
writeln!(glue, "/* luaconf.h */")?;
let pointer_bit_width: usize = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
.unwrap()
.parse()
.unwrap();
writeln!(
glue,
"pub const LUA_EXTRASPACE: c_int = {} / 8;",
pointer_bit_width
)?;
// This is generally hardcoded to this size
writeln!(glue, "pub const LUA_IDSIZE: c_int = 60;")?;
// Unless the target is restricted, the defaults are 64 bit
writeln!(glue, "pub type LUA_NUMBER = c_double;")?;
writeln!(glue, "pub type LUA_INTEGER = i64;")?;
writeln!(glue, "pub type LUA_UNSIGNED = u64;")?;
writeln!(glue, "/* lua.h */")?;
let version = if cfg!(any(feature = "luajit", feature = "lua51")) {
(5, 1, 0)
} else if cfg!(feature = "lua52") {
(5, 2, 0)
} else if cfg!(feature = "lua53") {
(5, 3, 0)
} else if cfg!(feature = "lua54") {
(5, 4, 0)
} else {
unreachable!();
};
writeln!(
glue,
"pub const LUA_VERSION_NUM: c_int = {};",
(version.0 * 100) + version.1
)?;
let max_stack = if pointer_bit_width >= 32 {
1_000_000
} else {
15_000
};
writeln!(
glue,
"pub const LUA_REGISTRYINDEX: c_int = -{} - 1000;",
max_stack
)?;
// These two are only defined in lua 5.1
writeln!(glue, "pub const LUA_ENVIRONINDEX: c_int = -10001;")?;
writeln!(glue, "pub const LUA_GLOBALSINDEX: c_int = -10002;")?;
writeln!(glue, "/* lauxlib.h */")?;
// This is only defined in lua 5.3 and up, but we can always generate its value here,
// even if we don't use it.
// This matches the default definition in lauxlib.h
writeln!(glue, "pub const LUAL_NUMSIZES: c_int = std::mem::size_of::<LUA_INTEGER>() as c_int * 16 + std::mem::size_of::<LUA_NUMBER>() as c_int;")?;
writeln!(glue, "/* lualib.h */")?;
write!(
glue,
r#"
#[cfg(feature = "luajit")]
pub const LUA_BITLIBNAME: &str = "bit";
#[cfg(not(feature = "luajit"))]
pub const LUA_BITLIBNAME: &str = "bit32";
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_IOLIBNAME: &str = "io";
pub const LUA_LOADLIBNAME: &str = "package";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_JITLIBNAME: &str = "jit";
pub const LUA_FFILIBNAME: &str = "ffi";
"#
)?;
Ok(())
}
fn main() {
#[cfg(not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)))]
compile_error!("You must enable one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(
feature = "lua54",
any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)
))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(
feature = "lua53",
any(feature = "lua52", feature = "lua51", feature = "luajit")
))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
// We don't support "vendored module" mode on windows
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
compile_error!(
"Vendored (static) builds are not supported for modules on Windows.\n"
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
let include_dir = find::probe_lua();
if env::var("TARGET").unwrap() != env::var("HOST").unwrap() {
generate_glue().unwrap();
} else {
build_glue(&include_dir);
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
}
println!("cargo:rerun-if-changed=build");
}
+8
View File
@@ -0,0 +1,8 @@
coverage:
status:
patch:
default:
only_pulls: true
project:
default:
only_pulls: true
+195
View File
@@ -0,0 +1,195 @@
## mlua v0.10 release notes
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
### New features
#### `'static` Lua types
In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
#### Truly `send` feature
In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
```rust
let lua = Lua::new();
lua.globals().set("i", 0)?;
let func = lua.load("i = i + ...").into_function()?;
std::thread::scope(|s| {
s.spawn(|| {
for i in 0..5 {
func.call::<()>(i).unwrap();
}
});
s.spawn(|| {
for i in 0..5 {
func.call::<()>(i).unwrap();
}
});
});
assert_eq!(lua.globals().get::<i32>("i")?, 20);
```
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
#### Register Rust functions with variable number of arguments
The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
```rust
let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
lua.globals().set("add", add).unwrap();
// Prints 50
lua.load(r#"print(add(5, 45))"#).exec().unwrap();
```
To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
#### Setting metatable for Lua builtin types
For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
```rust
let mt = lua.create_table()?;
mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
lua.set_type_metatable::<bool>(Some(mt));
lua.load("assert(tostring(true) == '2')").exec().unwrap();
```
### Improvements
#### New `ObjectLike` trait
The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
#### `Either<L, R>` enum
The `Either<L, R>` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
```rust
let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));
lua.globals().set("func", func).unwrap();
// Prints: received: 123
lua.load(r#"print(func(123))"#).exec().unwrap();
// Prints: bad argument #1: error converting Lua table to Either<i32, String>
lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
```
#### `Lua::exec_raw` helper to execute low-level Lua C API code
For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
```rust
let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
let sum: i64 = unsafe {
lua.exec_raw(&t, |state| {
// top of the stack: table `t`
let mut sum = 0;
// push nil as the first key
mlua::ffi::lua_pushnil(state);
while mlua::ffi::lua_next(state, -2) != 0 {
sum += mlua::ffi::lua_tointeger(state, -1);
// Remove the value, keep the key for the next iteration
mlua::ffi::lua_pop(state, 1);
}
mlua::ffi::lua_pop(state, 1);
mlua::ffi::lua_pushinteger(state, sum);
// top of the stack: sum
})
}?;
assert_eq!(sum, 15);
```
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
#### `anyhow` feature flag
The new `anyhow` feature flag adds `IntoLua` and `Into<mlua::Error>` implementation for the `anyhow::Error` type.
```rust
let f = lua.create_function(|_, ()| {
Err(anyhow!("error message"))?;
Ok(())
})?;
```
### Breaking changes
#### Scope changes
The following `Scope` methods were changed:
- Removed `Scope::create_any_userdata`
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
In mlua v0.9 this can cause read-after-free bug in some edge cases.
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
```rust
let data = "hello".to_string();
lua.scope(|scope| {
let ud = scope.create_any_userdata_ref(&data)?;
// We can only borrow scoped userdata using this method
ud.borrow_scoped::<String, ()>(|s| {
assert_eq!(s, "hello");
})?;
Ok(())
})?;
```
Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
#### String changes
Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
Lua instance can be destroyed while reference to the data is still alive:
```rust
let lua = Lua::new();
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
let s_ref: &str = s.to_str()?; // this is not safe!
drop(lua);
println!("{s_ref}"); // use after free!
```
To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
```rust
let lua = Lua::new();
let s: mlua::String = lua.create_string("hello, world")?;
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
drop(lua);
println!("{s_ref}"); // ok
```
The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
The same applies to `BorrowedBytes`.
Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
+361
View File
@@ -0,0 +1,361 @@
## mlua v0.9 release notes
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
### New features
#### 1. New Any UserData API
This is a long awaited feature that allows to register in Lua foreign types that cannot implement `UserData` trait because of the Rust orphan rules.
Now you can register any type that implements [`Any`] trait as a userdata type.
Consider the following example:
```rust
lua.register_userdata_type::<std::string::String>(|reg| {
reg.add_method("len", |_, this, ()| Ok(this.len()));
reg.add_method_mut("push", |_, this, s: String| {
this.push_str(&s);
Ok(())
});
reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
})?;
let s = lua.create_any_userdata("hello".to_string())?;
lua.load(chunk! {
print("s:len() is " .. $s:len())
$s:push(" world")
// Prints: hello, world
print($s)
})
.exec()?;
```
In this example we registered [`std::string::String`] as a userdata type with a set of methods and then created an instance of this type in Lua.
It's _not_ required to register a type before using the `Lua::create_any_userdata()` method, instead an empty metatable will be created for you.
You can also register the same type multiple times with different methods. Any previously created instances will share the old metatable, while new instances will have the new one.
The new set of API is called `any_userdata` because it allows to register types that implements [`Any`] trait.
[`std::string::String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
[`Any`]: https://doc.rust-lang.org/stable/std/any/trait.Any.html
#### 2. Scope support for the new any userdata types
When you need to create non-static userdata instances in Lua, the usual way is use `Lua::scope()` helper to make them scoped. When out of scope, any scoped objects will be automatically
dropped. The only downside of this approach is that every new instance will have a new metatable. This is not very fast if you need to create a lot of instances.
With the new Any UserData API, you can place non-static references `&T` where `T: 'static` into a scope and they will share a single static metatable.
```rust
lua.register_userdata_type::<std::string::String>(|reg| {
reg.add_method_mut("replace", |_, this, (pat, to): (String, String)| {
*this = this.replace(&pat, &to);
Ok(())
});
reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
})?;
let mut s = "hello, world".to_string();
lua.scope(|scope| {
// This userdata instance holds only a mutable reference to our string
let ud = scope.create_any_userdata_ref_mut(&mut s)?;
lua.load(chunk! {
$ud:replace("world", "user")
})
.exec()
})?;
// Prints: hello, user!
println!("{s}!");
```
#### 3. Owned types (`unstable`)
One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData`/ `OwnedThread`that are `'static` (no lifetime attached).
```rust
let lua = Lua::new();
struct MyStruct {
table: OwnedTable,
func: OwnedFunction,
}
let my_struct = MyStruct {
table: lua.globals().into_owned(),
func: lua
.create_function(|_, t: Table| Ok(format!("{t:#?}")))?
.into_owned(),
};
// It's safe to drop Lua!
drop(lua);
let result = my_struct.func.call::<_, String>(my_struct.table)?;
println!("{result}");
```
Prior to v0.9, it was possible to do by creating a reference to the Lua value in registry using `Lua::create_registry_value()`
and retrieving value later using `Lua::registry_value()` method.
All owned handles hold a *strong* reference to the current Lua instance.
Be warned, if you place them into a Lua type (eg. `UserData` or a Rust callback), it is *very easy*
to accidentally cause reference cycles that would prevent destroying Lua instance.
Please note this functionality is available under the `unstable` feature flag and not available when the `send` feature is enabled.
#### New ffi module
In v0.9 release the internal `ffi` module has been moved into the new [`mlua-sys`] crate and became available for public use.
This crate provides unified Lua FFI API (targeting Lua 5.4) using a (limited) compatibility layer for older versions.
mlua re-exports the `ffi` module aliasing the `mlua-sys` crate and provides (unsafe) functionality to work with raw Lua state:
```rust
unsafe {
unsafe extern "C-unwind" fn lua_add(state: *mut mlua::lua_State) -> i32 {
let a = mlua::ffi::luaL_checkinteger(state, 1);
let b = mlua::ffi::luaL_checkinteger(state, 2);
mlua::ffi::lua_pushinteger(state, a + b);
1
}
let add = lua.create_c_function(lua_add)?;
assert_eq!(add.call::<_, i32>((2, 3))?, 5);
}
```
[`mlua-sys`]: https://crates.io/crates/mlua-sys
#### Luau JIT support
mlua brings support for the new [Luau] JIT backend under the `luau-jit` feature flag.
It will automatically trigger JIT compilation for new Lua chunks. To disable it, just call `lua.enable_jit(false)` before loading Lua code
(but any previously compiled chunks will remain JIT-compiled).
[Luau]: https://luau-lang.org
### Improvements
#### 1. Better error reporting
When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported a error message without any context or reference to the particular argument.
In v0.9 it reports a error message with the argument index and expected type:
```rust
let func = lua.create_function(|_, _a: i32| Ok(()))?;
lua.load(chunk! {
local ok, err = pcall($func, "not a number")
// Prints: bad argument #1: error converting Lua string to i32 (expected number or string coercible to number)
print(err)
})
.exec()?;
```
Similar changes have been made for userdata functions and methods:
```rust
lua.register_userdata_type::<&'static str>(|reg| {
reg.add_method("len", |_, this, ()| Ok(this.len()));
})?;
let s = lua.create_any_userdata("hello")?;
lua.load(chunk! {
local ok, err = pcall($s.len, 123)
// Prints: bad argument `self` to `&str.len`: error converting Lua integer to userdata
print(err)
})
.exec()?;
```
#### 2. Error context
Similar to the [`anyhow`] Error type, now it's possible to attach context to Lua errors:
```rust
let read = lua.create_function(|lua, path: String| {
let bytes = std::fs::read(&path)
.into_lua_err()
.context(format!("Failed to open `{path}`"))?;
Ok(lua.create_string(bytes))
})?;
lua.load(chunk! {
local ok, err = pcall($read, "/nonexistent")
/// Prints:
/// Failed to open /nonexistent
/// No such file or directory (os error 2)
/// stack traceback:
/// ...
print(err)
})
.exec()?;
```
[`anyhow`]: https://crates.io/crates/anyhow
#### 4. New methods `Function::wrap`/`AnyUserData::wrap`
Sometimes it's useful to have `IntoLua` trait implementation for a Rust function or type `T: Any` without needing to call `Lua::create_function()`/`Lua::create_any_userdata()` methods.
Since v0.9 you can call the new methods `Function::wrap()`/`AnyUserData::wrap()` that allows to do this. They return an abstract type that `impl IntoLua`:
```rust
lua.globals().set("print_rust", Function::wrap(|_, s: String| Ok(println!("{}", s))))?;
lua.globals().set("rust_ud", AnyUserData::wrap("hello"))?;
```
In addition there are also `Function::wrap_mut()`/`Function::wrap_async()` methods that allow to wrap mutable and async functions respectively.
For a `T: 'UserData + 'static` the `IntoLua` trait is still always implemented.
#### `UserDataRef` and `UserDataRefMut` type wrappers
The new wrappers `UserDataRef` and `UserDataRefMut` are receivers for userdata type `T` and borrow underlying instance for the lifetime of the wrapper.
```rust
lua.globals()
.set("ud", AnyUserData::wrap("hello".to_string()))?;
let mut ud_mut: UserDataRefMut<String> = lua.globals().get("ud")?;
ud_mut.push_str(", Rust");
drop(ud_mut);
let ud_ref: UserDataRef<String> = lua.globals().get("ud")?;
// Prints: hello, Rust
println!("{}", *ud_ref);
```
In the previous mlua versions the same functionality can be achieved by receiving `AnyUserData` and calling `AnyUserData::borrow()`/`AnyUserData::borrow_mut()` methods.
The new wrappers are identical to Rust [`Ref`]/[`RefMut`] types.
[`Ref`]: https://doc.rust-lang.org/std/cell/struct.Ref.html
[`RefMut`]: https://doc.rust-lang.org/std/cell/struct.RefMut.html
#### New `AnyUserDataExt` trait
Similar to the `TableExt` trait, the `AnyUserDataExt` provides a set of extra methods for the `AnyUserData` type.
1) `AnyUserDataExt::get()/set()` to get/set a value by key from the userdata, assuming it has `__index` metamethod.
2) `AnyUserDataExt::call()` to call the userdata as a function assuming it has `__call` metamethod.
3) `AnyUserData::call_method(name, ...)` to call the userdata method, assuming it has `__index` metamethod and the associated function.
#### Pretty formatting Lua values
`mlua::Value` implements a new format `:#?` that allows to (recursively) pretty print Lua values:
```rust
println!("{:#?}", lua.globals());
```
Prints:
```
{
["_G"] = table: 0x7fa2d0706260,
["_VERSION"] = "Lua 5.4",
["assert"] = function: 0x10451d11d,
["collectgarbage"] = function: 0x10451d198,
["coroutine"] = {
["close"] = function: 0x10451e28f,
...
},
["dofile"] = function: 0x10451d37c,
...
}
```
In addition a new method `Value::to_string()` has been added to convert `Value` to a string (using `__tostring` metamethod if available).
#### Environment for Lua functions
Any Lua functions have an associated environment table that is used to resolve global variables. By default it sets to a Lua globals table.
In the new release it's possible to get or update a function environment using `Function::environment()` or `Function::set_environment()` methods respectively.
```rust
let f = lua.load("return a").into_function()?;
assert_eq!(f.environment(), Some(lua.globals()));
lua.globals().set("a", 1)?;
assert_eq!(f.call::<_, i32>(())?, 1);
f.set_environment(lua.create_table_from([("a", "hello")])?)?;
assert_eq!(f.call::<_, mlua::String>(())?, "hello");
```
#### Performance optimizations
The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
[benchmarks results]: https://github.com/mlua-rs/script-bench-rs
### Changes in `module` mode
#### New attributes
The `lua_module` macro now support the following attributes:
- `name=...` - sets name of the module (defaults to the name of the function).
Eg.:
```rust
#[mlua::lua_module(name = "alt_module")]
fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
lua.create_table()
}
```
Under the hood a new function `luaopen_alt_module` will be created for the Lua module loader.
- `skip_memory_check` - skip memory allocation checks for some operations.
In module mode, mlua runs in unknown environment and cannot say are there any memory limits or not. As result, some operations that require memory allocation runs in
protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
#### Improved Windows target
In previous mlua versions, building a Lua module for Windows requires having Lua development libraries installed on the system.
In contrast, on Linux and macOS, modules can be built without any external dependencies using the `-undefined=dynamic_lookup` linker flag.
With Rust 1.71+ it's now possible to lift this restriction for Windows as well. You can build modules normally and they will be linked with
`lua54.dll`/`lua53.dll`/`lua52.dll`/`lua51.dll` depending on the enabled Lua version.
You still need to have the dll although, linked to application where the module will be loaded.
### Breaking changes
1) `ToLua`/`ToLuaMulti` traits have been renamed to `IntoLua`/`IntoLuaMulti` respectively (with the methods called `into_lua`/`into_lua_multi`).
The main reason for this change is following the Rust self [convention](https://rust-lang.github.io/rust-clippy/master/index.html#/wrong_self_convention).
2) Removed `FromLua` implementation for `T: UserData + Clone`.
During the usage of mlua, it was found that this implementation is not very useful and prevents custom `FromLua` implementations for `T: UserData`.
It should be a developer decision to opt-in `FromLua` for their `T` if needed rather than having enabled it unconditionally.
To opt-in `FromLua` for `T: Clone` you can use a simple `#[derive(FromLua)]` macro (requires `feature = "macros"`):
```rust
#[derive(Clone, Copy, mlua::FromLua)]
struct MyUserData(i32);
```
`T` is not required to implement `UserData` because of the new relaxed restrictions on userdata types.
+23 -29
View File
@@ -1,42 +1,36 @@
use std::collections::HashMap;
use std::sync::Arc;
use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient;
use tokio::sync::Mutex;
use http_body_util::BodyExt as _;
use hyper::body::Incoming;
use hyper_util::client::legacy::Client as HyperClient;
use hyper_util::rt::TokioExecutor;
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
#[derive(Clone)]
struct BodyReader(Arc<Mutex<HyperBody>>);
impl BodyReader {
fn new(body: HyperBody) -> Self {
BodyReader(Arc::new(Mutex::new(body)))
}
}
struct BodyReader(Incoming);
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("read", |lua, reader, ()| async move {
let mut reader = reader.0.lock().await;
if let Some(bytes) = reader.data().await {
let bytes = bytes.to_lua_err()?;
return Some(lua.create_string(&bytes)).transpose();
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
// Every call returns a next chunk
methods.add_async_method_mut("read", |lua, mut reader, ()| async move {
if let Some(bytes) = reader.0.frame().await {
if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
return Some(lua.create_string(&bytes)).transpose();
}
}
Ok(None)
});
}
}
#[tokio::main]
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let lua = Lua::new();
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
let client = HyperClient::new();
let uri = uri.parse().to_lua_err()?;
let resp = client.get(uri).await.to_lua_err()?;
let client = HyperClient::builder(TokioExecutor::new()).build_http::<String>();
let uri = uri.parse().into_lua_err()?;
let resp = client.get(uri).await.into_lua_err()?;
let lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
@@ -46,11 +40,11 @@ async fn main() -> Result<()> {
headers
.entry(key.as_str())
.or_insert(Vec::new())
.push(value.to_str().to_lua_err()?);
.push(value.to_str().into_lua_err()?);
}
lua_resp.set("headers", headers)?;
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
lua_resp.set("body", BodyReader(resp.into_body()))?;
Ok(lua_resp)
})?;
@@ -58,18 +52,18 @@ async fn main() -> Result<()> {
let f = lua
.load(chunk! {
local res = $fetch_url(...)
print(res.status)
print("status: "..res.status)
for key, vals in pairs(res.headers) do
for _, val in ipairs(vals) do
print(key..": "..val)
end
end
repeat
local body = res.body:read()
if body then
print(body)
local chunk = res.body:read()
if chunk then
print(chunk)
end
until not body
until not chunk
})
.into_function()?;
+10 -16
View File
@@ -1,33 +1,27 @@
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
#[tokio::main]
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let lua = Lua::new();
let null = lua.null();
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
let resp = reqwest::get(&uri)
.await
.and_then(|resp| resp.error_for_status())
.to_lua_err()?;
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
.into_lua_err()?;
let json = resp.json::<serde_json::Value>().await.into_lua_err()?;
lua.to_value(&json)
})?;
let dbg = lua.create_function(|_, value: Value| {
println!("{value:#?}");
Ok(())
})?;
let f = lua
.load(chunk! {
function print_r(t, indent)
local indent = indent or ""
for k, v in pairs(t) do
io.write(indent, tostring(k))
if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
else io.write(": ", v == $null and "null" or tostring(v), "\n") end
end
end
local res = $fetch_json(...)
print_r(res)
$dbg(res)
})
.into_function()?;
+109 -80
View File
@@ -1,99 +1,128 @@
use std::convert::Infallible;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt as _, Empty, Full};
use hyper::body::{Bytes, Incoming};
use hyper::server::conn::http1;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataMethods};
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
struct LuaRequest(SocketAddr, Request<Body>);
/// Wrapper around incoming request that implements UserData
struct LuaRequest(SocketAddr, Request<Incoming>);
impl UserData for LuaRequest {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
}
}
async fn run_server(handler: Function<'static>) -> Result<()> {
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
let handler = handler.clone();
async move {
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
let handler = handler.clone();
async move {
let lua_req = LuaRequest(remote_addr, req);
let lua_resp: Table = handler.call_async(lua_req).await?;
let body = lua_resp
.get::<_, Option<String>>("body")?
.unwrap_or_default();
/// Service that handles incoming requests
#[derive(Clone)]
pub struct Svc {
handler: Function,
peer_addr: SocketAddr,
}
let mut resp = Response::builder()
.status(lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200));
impl Svc {
pub fn new(handler: Function, peer_addr: SocketAddr) -> Self {
Self { handler, peer_addr }
}
}
if let Some(headers) = lua_resp.get::<_, Option<Table>>("headers")? {
for pair in headers.pairs::<String, String>() {
impl hyper::service::Service<Request<Incoming>> for Svc {
type Response = Response<BoxBody<Bytes, Infallible>>;
type Error = LuaError;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, req: Request<Incoming>) -> Self::Future {
// If handler returns an error then generate 5xx response
let handler = self.handler.clone();
let lua_req = LuaRequest(self.peer_addr, req);
Box::pin(async move {
match handler.call_async::<Table>(lua_req).await {
Ok(lua_resp) => {
let status = lua_resp.get::<Option<u16>>("status")?.unwrap_or(200);
let mut resp = Response::builder().status(status);
// Set headers
if let Some(headers) = lua_resp.get::<Option<Table>>("headers")? {
for pair in headers.pairs::<String, LuaString>() {
let (h, v) = pair?;
resp = resp.header(&h, v);
resp = resp.header(&h, &*v.as_bytes());
}
}
Ok::<_, Error>(resp.body(Body::from(body)).unwrap())
// Set body
let body = lua_resp
.get::<Option<LuaString>>("body")?
.map(|b| Full::new(Bytes::copy_from_slice(&b.as_bytes())).boxed())
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
Ok(resp.body(body).unwrap())
}
Err(err) => {
eprintln!("{}", err);
Ok(Response::builder()
.status(500)
.body(Full::new(Bytes::from("Internal Server Error")).boxed())
.unwrap())
}
}))
}
});
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).executor(LocalExec).serve(make_svc);
println!("Listening on http://{}", addr);
tokio::task::LocalSet::new()
.run_until(server)
.await
.map_err(Error::external)
}
#[tokio::main]
async fn main() -> Result<()> {
let lua = Lua::new().into_static();
let handler: Function = lua
.load(
r#"
function(req)
return {
status = 200,
headers = {
["X-Req-Method"] = req:method(),
["X-Remote-Addr"] = req:remote_addr(),
},
body = "Hello, World!\n"
}
end
"#,
)
.eval()?;
run_server(handler).await?;
// Consume the static reference and drop it.
// This is safe as long as we don't hold any other references to Lua
// or alive resources.
unsafe { Lua::from_static(lua) };
Ok(())
}
#[derive(Clone, Copy, Debug)]
struct LocalExec;
impl<F> hyper::rt::Executor<F> for LocalExec
where
F: std::future::Future + 'static, // not requiring `Send`
{
fn execute(&self, fut: F) {
tokio::task::spawn_local(fut);
})
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let lua = Lua::new();
// Create Lua handler function
let handler = lua
.load(chunk! {
function(req)
return {
status = 200,
headers = {
["X-Req-Method"] = req:method(),
["X-Req-Path"] = req:path(),
["X-Remote-Addr"] = req:remote_addr(),
},
body = "Hello from Lua!\n"
}
end
})
.eval::<Function>()
.expect("Failed to create Lua handler");
let listen_addr = "127.0.0.1:3000";
let listener = TcpListener::bind(listen_addr).await.unwrap();
println!("Listening on http://{listen_addr}");
loop {
let (stream, peer_addr) = match listener.accept().await {
Ok(x) => x,
Err(err) => {
eprintln!("Failed to accept connection: {err}");
continue;
}
};
let svc = Svc::new(handler.clone(), peer_addr);
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(TokioIo::new(stream), svc)
.await
{
eprintln!("Error serving connection: {:?}", err);
}
});
}
}
+57 -88
View File
@@ -1,122 +1,91 @@
use std::sync::Arc;
use std::io;
use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::Mutex;
use tokio::task;
use mlua::{chunk, Function, Lua, Result, String as LuaString, UserData, UserDataMethods};
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
struct LuaTcp;
#[derive(Clone)]
struct LuaTcpListener(Arc<Mutex<TcpListener>>);
#[derive(Clone)]
struct LuaTcpStream(Arc<Mutex<TcpStream>>);
impl UserData for LuaTcp {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_function("bind", |_, addr: String| async move {
let listener = TcpListener::bind(addr).await?;
Ok(LuaTcpListener(Arc::new(Mutex::new(listener))))
});
}
}
impl UserData for LuaTcpListener {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("accept", |_, listener, ()| async move {
let (stream, _) = listener.0.lock().await.accept().await?;
Ok(LuaTcpStream(Arc::new(Mutex::new(stream))))
});
}
}
struct LuaTcpStream(TcpStream);
impl UserData for LuaTcpStream {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("peer_addr", |_, stream, ()| async move {
Ok(stream.0.lock().await.peer_addr()?.to_string())
});
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("peer_addr", |_, this, ()| Ok(this.0.peer_addr()?.to_string()));
methods.add_async_method("read", |lua, stream, size: usize| async move {
methods.add_async_method_mut("read", |lua, mut this, size| async move {
let mut buf = vec![0; size];
let n = stream.0.lock().await.read(&mut buf).await?;
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
});
methods.add_async_method("write", |_, stream, data: LuaString| async move {
let n = stream.0.lock().await.write(&data.as_bytes()).await?;
methods.add_async_method_mut("write", |_, mut this, data: BString| async move {
let n = this.0.write(&data).await?;
Ok(n)
});
methods.add_async_method("close", |_, stream, ()| async move {
stream.0.lock().await.shutdown().await?;
methods.add_async_method_mut("close", |_, mut this, ()| async move {
this.0.shutdown().await?;
Ok(())
});
}
}
async fn run_server(lua: &'static Lua) -> Result<()> {
let spawn = lua.create_function(move |_, func: Function| {
task::spawn_local(async move { func.call_async::<_, ()>(()).await });
Ok(())
})?;
async fn run_server(handler: Function) -> io::Result<()> {
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let listener = TcpListener::bind(addr).await.expect("cannot bind addr");
let tcp = LuaTcp;
println!("Listening on {}", addr);
let server = lua
loop {
let (stream, _) = match listener.accept().await {
Ok(res) => res,
Err(err) if is_transient_error(&err) => continue,
Err(err) => return Err(err),
};
let handler = handler.clone();
tokio::task::spawn(async move {
let stream = LuaTcpStream(stream);
if let Err(err) = handler.call_async::<()>(stream).await {
eprintln!("{}", err);
}
});
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let lua = Lua::new();
// Create Lua handler function
let handler = lua
.load(chunk! {
local addr = ...
local listener = $tcp.bind(addr)
print("listening on "..addr)
local accept_new = true
while true do
local stream = listener:accept()
function(stream)
local peer_addr = stream:peer_addr()
print("connected from "..peer_addr)
if not accept_new then
return
end
$spawn(function()
while true do
local data = stream:read(100)
data = data:match("^%s*(.-)%s*$") -- trim
print("["..peer_addr.."] "..data)
if data == "bye" then
stream:write("bye bye\n")
stream:close()
return
end
if data == "exit" then
stream:close()
accept_new = false
return
end
stream:write("echo: "..data.."\n")
while true do
local data = stream:read(100)
data = data:match("^%s*(.-)%s*$") // trim
print("["..peer_addr.."] "..data)
if data == "bye" then
stream:write("bye bye\n")
stream:close()
return
end
end)
stream:write("echo: "..data.."\n")
end
end
})
.into_function()?;
.eval::<Function>()
.expect("cannot create Lua handler");
task::LocalSet::new()
.run_until(server.call_async::<_, ()>("0.0.0.0:1234"))
.await
run_server(handler).await.expect("cannot run server")
}
#[tokio::main]
async fn main() {
let lua = Lua::new().into_static();
run_server(lua).await.unwrap();
// Consume the static reference and drop it.
// This is safe as long as we don't hold any other references to Lua
// or alive resources.
unsafe { Lua::from_static(lua) };
fn is_transient_error(e: &io::Error) -> bool {
e.kind() == io::ErrorKind::ConnectionRefused
|| e.kind() == io::ErrorKind::ConnectionAborted
|| e.kind() == io::ErrorKind::ConnectionReset
}
+26 -19
View File
@@ -1,7 +1,7 @@
use std::f32;
use std::iter::FromIterator;
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
fn main() -> Result<()> {
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
@@ -17,12 +17,12 @@ fn main() -> Result<()> {
globals.set("string_var", "hello")?;
globals.set("int_var", 42)?;
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
assert_eq!(globals.get::<String>("string_var")?, "hello");
assert_eq!(globals.get::<i64>("int_var")?, 42);
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
// that allows you to change settings before running Lua code. Here, we are using it to set
// the name of the laoded chunk to "example code", which will be used when Lua error
// the name of the loaded chunk to "example code", which will be used when Lua error
// messages are printed.
lua.load(
@@ -30,9 +30,9 @@ fn main() -> Result<()> {
global = 'foo'..'bar'
"#,
)
.set_name("example code")?
.set_name("example code")
.exec()?;
assert_eq!(globals.get::<_, String>("global")?, "foobar");
assert_eq!(globals.get::<String>("global")?, "foobar");
assert_eq!(lua.load("1 + 1").eval::<i32>()?, 2);
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
@@ -85,20 +85,20 @@ fn main() -> Result<()> {
// You can load Lua functions
let print: Function = globals.get("print")?;
print.call::<_, ()>("hello from rust")?;
print.call::<()>("hello from rust")?;
// This API generally handles variadics using tuples. This is one way to call a function with
// This API generally handles variadic using tuples. This is one way to call a function with
// multiple parameters:
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
print.call::<()>(("hello", "again", "from", "rust"))?;
// But, you can also pass variadic arguments with the `Variadic` type.
print.call::<_, ()>(Variadic::from_iter(
print.call::<()>(Variadic::from_iter(
["hello", "yet", "again", "from", "rust"].iter().cloned(),
))?;
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
// You can bind rust functions to Lua as well. Callbacks receive the Lua state itself as their
// first parameter, and the arguments given to the function as the second parameter. The type
// of the arguments can be anything that is convertible from the parameters given by Lua, in
// this case, the function expects two string sequences.
@@ -151,8 +151,18 @@ fn main() -> Result<()> {
#[derive(Copy, Clone)]
struct Vec2(f32, f32);
// We can implement `FromLua` trait for our `Vec2` to return a copy
impl FromLua for Vec2 {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => unreachable!(),
}
}
}
impl UserData for Vec2 {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("magnitude", |_, vec, ()| {
let mag_squared = vec.0 * vec.0 + vec.1 * vec.1;
Ok(mag_squared.sqrt())
@@ -167,19 +177,15 @@ fn main() -> Result<()> {
let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y)))?;
globals.set("vec2", vec2_constructor)?;
assert!(
(lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()")
.eval::<f32>()?
- 5.0)
.abs()
< f32::EPSILON
);
assert!((lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()").eval::<f32>()? - 5.0).abs() < f32::EPSILON);
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
// TODO: Re-enable this
/*
{
let mut rust_val = 0;
@@ -201,6 +207,7 @@ fn main() -> Result<()> {
assert_eq!(rust_val, 42);
}
*/
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
// try to run our 'sketchy' function outside of the scope, the function we created will have
+6
View File
@@ -3,3 +3,9 @@ rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
+3 -2
View File
@@ -2,18 +2,19 @@
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[workspace]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../..", features = ["module"] }
-22
View File
@@ -8,32 +8,10 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
Ok(ud.0)
}
#[mlua::lua_module]
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
Ok(exports)
}
#[derive(Clone, Copy)]
struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module]
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
Ok(exports)
}
#[mlua::lua_module]
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".to_lua_err())
}
+4 -4
View File
@@ -1,11 +1,11 @@
//! This example shows a simple read-evaluate-print-loop (REPL).
use mlua::{Error, Lua, MultiValue};
use rustyline::Editor;
use rustyline::DefaultEditor;
fn main() {
let lua = Lua::new();
let mut editor = Editor::<()>::new();
let mut editor = DefaultEditor::new().expect("Failed to create editor");
loop {
let mut prompt = "> ";
@@ -19,12 +19,12 @@ fn main() {
match lua.load(&line).eval::<MultiValue>() {
Ok(values) => {
editor.add_history_entry(line);
editor.add_history_entry(line).unwrap();
println!(
"{}",
values
.iter()
.map(|value| format!("{:?}", value))
.map(|value| format!("{:#?}", value))
.collect::<Vec<_>>()
.join("\t")
);
+7 -2
View File
@@ -28,9 +28,14 @@ fn main() -> Result<()> {
let globals = lua.globals();
// Create Car struct from a Lua table
let car: Car = lua.from_value(lua.load(r#"
let car: Car = lua.from_value(
lua.load(
r#"
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
"#).eval()?)?;
"#,
)
.eval()?,
)?;
// Set it as (serializable) userdata
globals.set("null", lua.null())?;
+2 -2
View File
@@ -7,7 +7,7 @@ struct Rectangle {
}
impl UserData for Rectangle {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("length", |_, this| Ok(this.length));
fields.add_field_method_set("length", |_, this, val| {
this.length = val;
@@ -20,7 +20,7 @@ impl UserData for Rectangle {
});
}
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
methods.add_method("diagonal", |_, this, ()| {
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
+46
View File
@@ -0,0 +1,46 @@
[package]
name = "mlua-sys"
version = "0.7.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
rust-version = "1.71"
edition = "2021"
repository = "https://github.com/mlua-rs/mlua"
documentation = "https://docs.rs/mlua-sys"
readme = "README.md"
categories = ["external-ffi-bindings"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored"]
rustdoc-args = ["--cfg", "docsrs"]
[features]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
luau-codegen = ["luau"]
luau-vector4 = ["luau"]
vendored = ["lua-src", "luajit-src"]
module = []
[dependencies]
[build-dependencies]
cc = "1.0"
cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 547.1.0, < 547.2.0", optional = true }
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
luau0-src = { version = "0.14.2", optional = true }
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
+8
View File
@@ -0,0 +1,8 @@
# mlua-sys
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and [Luau].
Intended to be consumed by the [mlua] crate.
[Luau]: https://github.com/luau-lang/luau
[mlua]: https://crates.io/crates/mlua
+68
View File
@@ -0,0 +1,68 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
pub fn probe_lua() {
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
if target_arch == "wasm32" && cfg!(not(feature = "vendored")) {
panic!("Please enable `vendored` feature to build for wasm32");
}
let lib_dir = env::var("LUA_LIB").unwrap_or_default();
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or_default();
println!("cargo:rerun-if-env-changed=LUA_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK");
if !lua_lib.is_empty() {
if !lib_dir.is_empty() {
println!("cargo:rustc-link-search=native={lib_dir}");
}
let mut link_lib = "";
if env::var("LUA_LINK").as_deref() == Ok("static") {
link_lib = "static=";
};
println!("cargo:rustc-link-lib={link_lib}{lua_lib}");
return;
}
// Find using `pkg-config`
#[cfg(feature = "lua54")]
let (incl_bound, excl_bound, alt_probe, ver) =
("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
#[cfg(feature = "lua53")]
let (incl_bound, excl_bound, alt_probe, ver) =
("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
#[cfg(feature = "lua52")]
let (incl_bound, excl_bound, alt_probe, ver) =
("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
#[cfg(feature = "lua51")]
let (incl_bound, excl_bound, alt_probe, ver) =
("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
#[cfg(feature = "luajit")]
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
#[rustfmt::skip]
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
.cargo_metadata(true)
.probe(if cfg!(feature = "luajit") { "luajit" } else { "lua" });
if lua.is_err() {
for pkg in alt_probe {
lua = pkg_config::Config::new()
.cargo_metadata(true)
.probe(pkg);
if lua.is_ok() {
break;
}
}
}
lua.unwrap_or_else(|err| panic!("cannot find Lua{ver} using `pkg-config`: {err}"));
}
@@ -1,19 +1,29 @@
use std::path::PathBuf;
#![allow(dead_code)]
pub fn probe_lua() -> PathBuf {
pub fn probe_lua() {
#[cfg(feature = "lua54")]
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
#[cfg(feature = "lua53")]
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
#[cfg(feature = "lua52")]
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
#[cfg(feature = "lua51")]
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
#[cfg(feature = "luajit")]
let artifacts = luajit_src::Build::new().build();
let artifacts = luajit_src::Build::new()
.lua52compat(cfg!(feature = "luajit52"))
.build();
#[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new()
.enable_codegen(cfg!(feature = "luau-codegen"))
.set_max_cstack_size(1000000)
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
.build();
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata();
artifacts.include_dir().to_owned()
}
+19
View File
@@ -0,0 +1,19 @@
cfg_if::cfg_if! {
if #[cfg(all(feature = "lua54", not(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
include!("main_inner.rs");
} else if #[cfg(all(feature = "luau", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
include!("main_inner.rs");
} else {
fn main() {
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau");
}
}
}
+35
View File
@@ -0,0 +1,35 @@
use std::env;
cfg_if::cfg_if! {
if #[cfg(any(feature = "luau", feature = "vendored"))] {
#[path = "find_vendored.rs"]
mod find;
} else {
#[path = "find_normal.rs"]
mod find;
}
}
fn main() {
#[cfg(all(feature = "luau", feature = "module", windows))]
compile_error!("Luau does not support `module` mode on Windows");
#[cfg(all(feature = "module", feature = "vendored"))]
compile_error!("`vendored` and `module` features are mutually exclusive");
println!("cargo:rerun-if-changed=build");
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "windows" && cfg!(feature = "module") {
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
// Don't use raw-dylib linking
find::probe_lua();
return;
}
println!("cargo:rustc-cfg=raw_dylib");
}
#[cfg(not(feature = "module"))]
find::probe_lua();
}
+105
View File
@@ -0,0 +1,105 @@
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau.
#![allow(non_camel_case_types, non_snake_case, dead_code)]
#![allow(clippy::missing_safety_doc)]
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::os::raw::c_int;
#[cfg(any(feature = "lua54", doc))]
pub use lua54::*;
#[cfg(any(feature = "lua53", doc))]
pub use lua53::*;
#[cfg(any(feature = "lua52", doc))]
pub use lua52::*;
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
pub use lua51::*;
#[cfg(any(feature = "luau", doc))]
pub use luau::*;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 255;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 60;
#[cfg(feature = "luau")]
#[doc(hidden)]
pub const LUA_MAX_UPVALUES: c_int = 200;
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
// 5.4 uses `luaL_Buffer`
#[doc(hidden)]
pub const LUA_TRACEBACK_STACK: c_int = 11;
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/common/alloc.rs
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
#[cfg(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "m68k",
target_arch = "csky",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc",
target_arch = "wasm32",
target_arch = "hexagon",
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
all(target_arch = "xtensa", not(target_os = "espidf")),
))]
#[doc(hidden)]
pub const SYS_MIN_ALIGN: usize = 8;
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "loongarch64",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "s390x",
target_arch = "sparc64",
target_arch = "riscv64",
target_arch = "wasm64",
))]
#[doc(hidden)]
pub const SYS_MIN_ALIGN: usize = 16;
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
#[cfg(any(
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
all(target_arch = "xtensa", target_os = "espidf"),
))]
#[doc(hidden)]
pub const SYS_MIN_ALIGN: usize = 4;
#[macro_use]
mod macros;
#[cfg(any(feature = "lua54", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
pub mod lua54;
#[cfg(any(feature = "lua53", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua53")))]
pub mod lua53;
#[cfg(any(feature = "lua52", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lua52")))]
pub mod lua52;
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua51", feature = "luajit"))))]
pub mod lua51;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub mod luau;
+576
View File
@@ -0,0 +1,576 @@
//! MLua compatibility layer for Lua 5.1/JIT
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::os::raw::{c_char, c_int, c_void};
use std::{mem, ptr};
use super::lauxlib::*;
use super::lua::*;
#[inline(always)]
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
while a < b {
lua_pushvalue(L, a);
lua_pushvalue(L, b);
lua_replace(L, a);
lua_replace(L, b);
a += 1;
b -= 1;
}
}
const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack
const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack
unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int {
let mut ar: lua_Debug = mem::zeroed();
let (mut li, mut le) = (1, 1);
// find an upper bound
while lua_getstack(L, le, &mut ar) != 0 {
li = le;
le *= 2;
}
// do a binary search
while li < le {
let m = (li + le) / 2;
if lua_getstack(L, m, &mut ar) != 0 {
li = m + 1
} else {
le = m;
}
}
le - 1
}
unsafe fn compat53_checkmode(
L: *mut lua_State,
mode: *const c_char,
modename: *const c_char,
err: c_int,
) -> c_int {
unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char {
let mut st = s;
while *st != 0 && *st != c {
st = st.offset(1);
}
if *st == c {
st
} else {
ptr::null()
}
}
if !mode.is_null() && strchr(mode, *modename).is_null() {
lua_pushfstring(
L,
cstr!("attempt to load a %s chunk (mode is '%s')"),
modename,
mode,
);
return err;
}
LUA_OK
}
unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int {
if level == 0 || lua_istable(L, -1) == 0 {
return 0; // not found
}
lua_pushnil(L); // start 'next' loop
while lua_next(L, -2) != 0 {
// for each pair in table
if lua_type(L, -2) == LUA_TSTRING {
// ignore non-string keys
if lua_rawequal(L, objidx, -1) != 0 {
// found object?
lua_pop(L, 1); // remove value (but keep name)
return 1;
} else if compat53_findfield(L, objidx, level - 1) != 0 {
// try recursively
lua_remove(L, -2); // remove table (but keep name)
lua_pushliteral(L, c".");
lua_insert(L, -2); // place '.' between the two names
lua_concat(L, 3);
return 1;
}
}
lua_pop(L, 1); // remove value
}
0 // not found
}
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> c_int {
let top = lua_gettop(L);
lua_getinfo(L, cstr!("f"), ar); // push function
lua_pushvalue(L, LUA_GLOBALSINDEX);
if compat53_findfield(L, top + 1, 2) != 0 {
lua_copy(L, -1, top + 1); // move name to proper place
lua_pop(L, 2); // remove pushed values
1
} else {
lua_settop(L, top); // remove function and global table
0
}
}
unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) {
if *(*ar).namewhat != b'\0' as c_char {
// is there a name?
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
} else if *(*ar).what == b'm' as c_char {
// main?
lua_pushliteral(L, c"main chunk");
} else if *(*ar).what == b'C' as c_char {
if compat53_pushglobalfuncname(L, ar) != 0 {
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
lua_remove(L, -2); // remove name
} else {
lua_pushliteral(L, c"?");
}
} else {
lua_pushfstring(
L,
cstr!("function <%s:%d>"),
(*ar).short_src.as_ptr(),
(*ar).linedefined,
);
}
}
//
// lua ported functions
//
#[inline(always)]
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
if idx < 0 && idx > LUA_REGISTRYINDEX {
idx += lua_gettop(L) + 1;
}
idx
}
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
}
if n > 0 && n < n_elems {
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
n = n_elems - n;
compat53_reverse(L, idx, idx + n - 1);
compat53_reverse(L, idx + n, idx + n_elems - 1);
compat53_reverse(L, idx, idx + n_elems - 1);
}
}
#[inline(always)]
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
let abs_to = lua_absindex(L, toidx);
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
#[inline(always)]
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
return 1;
}
}
0
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
let n = lua_tonumber(L, i);
if !isnum.is_null() {
*isnum = (n != 0.0 || lua_isnumber(L, i) != 0) as c_int;
}
n
}
#[inline(always)]
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);
let n_int = n as lua_Integer;
if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if !isnum.is_null() {
*isnum = 1;
}
return n_int;
}
if !isnum.is_null() {
*isnum = 0;
}
0
}
#[inline(always)]
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
lua_objlen(L, idx)
}
#[inline(always)]
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
if l == 0 {
lua_pushlstring_(L, cstr!(""), 0);
} else {
lua_pushlstring_(L, s, l);
}
lua_tostring(L, -1)
}
#[inline(always)]
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
lua_pushstring_(L, s);
lua_tostring(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getglobal_(L, var);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
lua_gettable_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
lua_getfield_(L, idx, k);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_gettable(L, idx)
}
#[inline(always)]
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
lua_rawget_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
let n = n.try_into().expect("cannot convert index to lua_Integer");
lua_rawgeti_(L, idx, n);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
let abs_i = lua_absindex(L, idx);
lua_pushlightuserdata(L, p as *mut c_void);
lua_rawget(L, abs_i)
}
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getfenv(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_insert(L, -2);
lua_settable(L, idx);
}
#[inline(always)]
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawseti_(L, idx, n)
}
#[inline(always)]
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
}
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
luaL_checktype(L, -1, LUA_TTABLE);
lua_setfenv(L, idx);
}
#[inline(always)]
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
lua_dump_(L, writer, data)
}
#[inline(always)]
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
match lua_type(L, idx) {
LUA_TSTRING => {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
LUA_TTABLE => {
if luaL_callmeta(L, idx, cstr!("__len")) == 0 {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
}
LUA_TUSERDATA if luaL_callmeta(L, idx, cstr!("__len")) != 0 => {}
_ => {
luaL_error(
L,
cstr!("attempt to get length of a %s value"),
lua_typename(L, lua_type(L, idx)),
);
}
}
}
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
lua_pushvalue(L, LUA_GLOBALSINDEX);
}
#[inline(always)]
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
}
ret
}
//
// lauxlib ported functions
//
#[inline(always)]
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
if lua_checkstack(L, sz + LUA_MINSTACK) == 0 {
if !msg.is_null() {
luaL_error(L, cstr!("stack overflow (%s)"), msg);
} else {
lua_pushliteral(L, c"stack overflow");
lua_error(L);
}
}
}
#[inline(always)]
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
if luaL_getmetafield_(L, obj, e) != 0 {
lua_type(L, -1)
} else {
LUA_TNIL
}
}
#[inline(always)]
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
if luaL_newmetatable_(L, tname) != 0 {
lua_pushstring_(L, tname);
lua_setfield(L, -2, cstr!("__name"));
1
} else {
0
}
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setfenv(L, -2);
}
status
}
#[inline(always)]
pub unsafe fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int {
let status = if sz > 0 && *buff as u8 == LUA_SIGNATURE[0] {
compat53_checkmode(L, mode, cstr!("binary"), LUA_ERRSYNTAX)
} else {
compat53_checkmode(L, mode, cstr!("text"), LUA_ERRSYNTAX)
};
if status != LUA_OK {
return status;
}
luaL_loadbuffer(L, buff, sz, name)
}
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
if isnum == 0 {
luaL_error(L, cstr!("object length is not an integer"));
}
res
}
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
let mut ar: lua_Debug = mem::zeroed();
let top = lua_gettop(L);
let numlevels = compat53_countlevels(L1);
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
COMPAT53_LEVELS1
} else {
0
};
if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg);
}
lua_pushliteral(L, c"stack traceback:");
while lua_getstack(L1, level, &mut ar) != 0 {
level += 1;
if level == mark {
// too many levels?
lua_pushliteral(L, c"\n\t..."); // add a '...'
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
} else {
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
if ar.currentline > 0 {
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
}
lua_pushliteral(L, c" in ");
compat53_pushfuncname(L, &mut ar);
lua_concat(L, lua_gettop(L) - top);
}
}
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
LUA_TNIL => {
lua_pushliteral(L, c"nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
LUA_TBOOLEAN => {
if lua_toboolean(L, idx) == 0 {
lua_pushliteral(L, c"false");
} else {
lua_pushliteral(L, c"true");
}
}
t => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
} else {
lua_typename(L, t)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__name'
}
}
};
} else if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
lua_tolstring(L, -1, len)
}
#[inline(always)]
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
}
lua_pop(L, 1);
lua_newtable(L);
lua_pushstring_(L, fname);
lua_pushvalue(L, -2);
lua_settable(L, abs_i);
0
}
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
if lua_getfield(L, -1, modname) == LUA_TNIL {
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);
#[cfg(feature = "lua51")]
{
lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
}
#[cfg(feature = "luajit")]
{
lua_call(L, 1, 0);
lua_getfield(L, -1, modname);
}
}
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
+145
View File
@@ -0,0 +1,145 @@
//! Contains definitions from `lauxlib.h`.
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
// Extra error code for 'luaL_load'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
// Key, in the registry, for table of loaded modules
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_typerror(L: *mut lua_State, narg: c_int, tname: *const c_char) -> c_int;
pub fn luaL_argerror(L: *mut lua_State, narg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, narg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
narg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, narg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, narg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer;
#[link_name = "luaL_checkstack"]
pub fn luaL_checkstack_(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, narg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, narg: c_int);
#[link_name = "luaL_newmetatable"]
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
pub fn luaL_checkoption(
L: *mut lua_State,
narg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
}
// Pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
pub fn luaL_loadbuffer(L: *mut lua_State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
p: *const c_char,
r: *const c_char,
) -> *const c_char;
pub fn luaL_findtable(
L: *mut lua_State,
idx: c_int,
fname: *const c_char,
szhint: c_int,
) -> *const c_char;
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, narg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, narg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
// Deprecated from 5.3: luaL_checkint, luaL_optint, luaL_checklong, luaL_optlong
#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
lua::lua_typename(L, lua::lua_type(L, i))
}
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
let status = luaL_loadfile(L, filename);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
let status = luaL_loadstring(L, s);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
}
// TODO: luaL_opt
//
// TODO: Generic Buffer Manipulation
//
+401
View File
@@ -0,0 +1,401 @@
//! Contains definitions from `lua.h`.
use std::ffi::CStr;
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_int, c_void};
use std::ptr;
// Mark for precompiled code (`<esc>Lua`)
#[cfg(not(feature = "luajit"))]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
#[cfg(feature = "luajit")]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLJ";
// Option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -10000;
pub const LUA_ENVIRONINDEX: c_int = -10001;
pub const LUA_GLOBALSINDEX: c_int = -10002;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_GLOBALSINDEX - i
}
//
// Thread status
//
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRERR: c_int = 5;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
/// Type produced by LuaJIT FFI module
#[cfg(feature = "luajit")]
pub const LUA_TCDATA: c_int = 10;
/// Minimum Lua stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
/// A Lua number, usually equivalent to `f64`
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions (no unwinding)
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
//
// State manipulation
//
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
//
// Basic stack manipulation
//
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_remove(L: *mut lua_State, idx: c_int);
pub fn lua_insert(L: *mut lua_State, idx: c_int);
pub fn lua_replace(L: *mut lua_State, idx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
//
// Access functions (stack -> C)
//
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_equal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number;
#[link_name = "lua_tointeger"]
pub fn lua_tointeger_(L: *mut lua_State, idx: c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
//
// Push functions (C -> stack)
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
#[link_name = "lua_pushstring"]
pub fn lua_pushstring_(L: *mut lua_State, s: *const c_char);
// lua_pushvfstring
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
//
// Get functions (Lua -> stack)
//
#[link_name = "lua_gettable"]
pub fn lua_gettable_(L: *mut lua_State, idx: c_int);
#[link_name = "lua_getfield"]
pub fn lua_getfield_(L: *mut lua_State, idx: c_int, k: *const c_char);
#[link_name = "lua_rawget"]
pub fn lua_rawget_(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawgeti"]
pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_getfenv(L: *mut lua_State, idx: c_int);
//
// Set functions (stack -> Lua)
//
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawseti"]
pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
pub fn lua_cpcall(L: *mut lua_State, f: lua_CFunction, ud: *mut c_void) -> c_int;
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
data: *mut c_void,
chunkname: *const c_char,
) -> c_int;
#[link_name = "lua_dump"]
pub fn lua_dump_(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int;
//
// Coroutine functions
//
pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int;
#[link_name = "lua_resume"]
pub fn lua_resume_(L: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
}
//
// Garbage-collection function and options
//
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
//
// Miscellaneous functions
//
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
#[link_name = "lua_error"]
fn lua_error_(L: *mut lua_State) -> c_int;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
}
// lua_error does not return but is declared to return int, and Rust translates
// ! to void which can cause link-time errors if the platform linker is aware
// of return types and requires they match (for example: wasm does this).
#[inline(always)]
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
lua_error_(L);
unreachable!();
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
lua_pushcfunction(L, f);
lua_setglobal(L, n)
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosure(L, f, 0)
}
// TODO: lua_strlen
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
lua_pushstring_(L, s.as_ptr());
}
#[inline(always)]
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
lua_setfield(L, LUA_GLOBALSINDEX, var)
}
#[inline(always)]
pub unsafe fn lua_getglobal_(L: *mut lua_State, var: *const c_char) {
lua_getfield_(L, LUA_GLOBALSINDEX, var)
}
#[inline(always)]
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
if lua_islightuserdata(L, idx) != 0 {
return lua_touserdata(L, idx);
}
ptr::null_mut()
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
// Maximum size for the description of the source of a function in debug information.
const LUA_IDSIZE: usize = 60;
// Event codes
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
// Event masks
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
}
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub currentline: c_int,
pub nups: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub short_src: [c_char; LUA_IDSIZE],
// lua.h mentions this is for private use
i_ci: c_int,
}
+43
View File
@@ -0,0 +1,43 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_int};
use super::lua::lua_State;
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
#[cfg(feature = "luajit")]
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit");
#[cfg(feature = "luajit")]
pub const LUA_JITLIBNAME: *const c_char = cstr!("jit");
#[cfg(feature = "luajit")]
pub const LUA_FFILIBNAME: *const c_char = cstr!("ffi");
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_bit(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_jit(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_ffi(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+11
View File
@@ -0,0 +1,11 @@
//! Low level bindings to Lua 5.1.
pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use lualib::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod lualib;
+268
View File
@@ -0,0 +1,268 @@
//! MLua compatibility layer for Lua 5.2
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lauxlib::*;
use super::lua::*;
#[inline(always)]
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
while a < b {
lua_pushvalue(L, a);
lua_pushvalue(L, b);
lua_replace(L, a);
lua_replace(L, b);
a += 1;
b -= 1;
}
}
//
// lua ported functions
//
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
}
if n > 0 && n < n_elems {
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
n = n_elems - n;
compat53_reverse(L, idx, idx + n - 1);
compat53_reverse(L, idx + n, idx + n_elems - 1);
compat53_reverse(L, idx, idx + n_elems - 1);
}
}
#[inline(always)]
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
return 1;
}
}
0
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
// Implemented for Lua 5.2 as well
// See https://github.com/keplerproject/lua-compat-5.3/issues/40
#[inline(always)]
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);
let n_int = n as lua_Integer;
if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if !isnum.is_null() {
*isnum = 1;
}
return n_int;
}
if !isnum.is_null() {
*isnum = 0;
}
0
}
#[inline(always)]
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
if l == 0 {
lua_pushlstring_(L, cstr!(""), 0)
} else {
lua_pushlstring_(L, s, l)
}
}
#[inline(always)]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getglobal_(L, var);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
lua_gettable_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
lua_getfield_(L, idx, k);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_gettable(L, idx)
}
#[inline(always)]
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
lua_rawget_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
let n = n.try_into().expect("cannot convert index to lua_Integer");
lua_rawgeti_(L, idx, n);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
lua_rawgetp_(L, idx, p);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getuservalue_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_insert(L, -2);
lua_settable(L, idx);
}
#[inline(always)]
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawseti_(L, idx, n)
}
#[inline(always)]
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
lua_dump_(L, writer, data)
}
#[inline(always)]
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, from, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
}
ret
}
//
// lauxlib ported functions
//
#[inline(always)]
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
if luaL_getmetafield_(L, obj, e) != 0 {
lua_type(L, -1)
} else {
LUA_TNIL
}
}
#[inline(always)]
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
if luaL_newmetatable_(L, tname) != 0 {
lua_pushstring(L, tname);
lua_setfield(L, -2, cstr!("__name"));
1
} else {
0
}
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
LUA_TNIL => {
lua_pushliteral(L, c"nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
LUA_TBOOLEAN => {
if lua_toboolean(L, idx) == 0 {
lua_pushliteral(L, c"false");
} else {
lua_pushliteral(L, c"true");
}
}
t => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
} else {
lua_typename(L, t)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__name'
}
}
};
} else if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
lua_tolstring(L, -1, len)
}
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
if lua_getfield(L, -1, modname) == LUA_TNIL {
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);
lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
}
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setupvalue(L, -2, 1);
}
status
}
+178
View File
@@ -0,0 +1,178 @@
//! Contains definitions from `lauxlib.h`.
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned};
// Extra error code for 'luaL_load'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
// Key, in the registry, for table of loaded modules
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
// Key, in the registry, for table of preloaded loaders
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number);
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[link_name = "luaL_tolstring"]
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
-> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;
pub fn luaL_checkunsigned(L: *mut lua_State, arg: c_int) -> lua_Unsigned;
pub fn luaL_optunsigned(L: *mut lua_State, arg: c_int, def: lua_Unsigned) -> lua_Unsigned;
pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, arg: c_int);
#[link_name = "luaL_newmetatable"]
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
pub fn luaL_checkoption(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
}
// Pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}
#[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
p: *const c_char,
r: *const c_char,
) -> *const c_char;
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
#[link_name = "luaL_requiref"]
pub fn luaL_requiref_(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}
//
// Some useful macros (implemented as Rust functions)
//
// TODO: luaL_newlibtable, luaL_newlib
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, arg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
lua::lua_typename(L, lua::lua_type(L, i))
}
#[inline(always)]
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
let status = luaL_loadfile(L, filename);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
let status = luaL_loadstring(L, s);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
}
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
//
// TODO: Generic Buffer Manipulation
//
+485
View File
@@ -0,0 +1,485 @@
//! Contains definitions from `lua.h`.
use std::ffi::CStr;
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_uint, c_void};
use std::ptr;
// Mark for precompiled code (`<esc>Lua`)
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
// Option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
// Size of the Lua stack
pub const LUAI_MAXSTACK: c_int = 1000000;
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXSTACK - 1000;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i
}
//
// Thread status
//
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRGCMM: c_int = 5;
pub const LUA_ERRERR: c_int = 6;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
pub const LUA_NUMTAGS: c_int = 9;
/// Minimum Lua stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
// Predefined values in the registry
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1;
pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
/// A Lua number, usually equivalent to `f64`
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// A Lua unsigned integer, equivalent to `u32` in Lua 5.2
pub type lua_Unsigned = c_uint;
/// Type for native C functions that can be passed to Lua
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions (no unwinding)
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
//
// State manipulation
//
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
//
// Basic stack manipulation
//
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_remove(L: *mut lua_State, idx: c_int);
pub fn lua_insert(L: *mut lua_State, idx: c_int);
pub fn lua_replace(L: *mut lua_State, idx: c_int);
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
//
// Access functions (stack -> C)
//
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
#[link_name = "lua_tointegerx"]
pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
}
//
// Comparison and arithmetic functions
//
pub const LUA_OPADD: c_int = 0;
pub const LUA_OPSUB: c_int = 1;
pub const LUA_OPMUL: c_int = 2;
pub const LUA_OPDIV: c_int = 3;
pub const LUA_OPMOD: c_int = 4;
pub const LUA_OPPOW: c_int = 5;
pub const LUA_OPUNM: c_int = 6;
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Push functions (C -> stack)
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char;
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
// lua_pushvfstring
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
//
// Get functions (Lua -> stack)
//
#[link_name = "lua_getglobal"]
pub fn lua_getglobal_(L: *mut lua_State, name: *const c_char);
#[link_name = "lua_gettable"]
pub fn lua_gettable_(L: *mut lua_State, idx: c_int);
#[link_name = "lua_getfield"]
pub fn lua_getfield_(L: *mut lua_State, idx: c_int, k: *const c_char);
#[link_name = "lua_rawget"]
pub fn lua_rawget_(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawgeti"]
pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int);
#[link_name = "lua_rawgetp"]
pub fn lua_rawgetp_(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
#[link_name = "lua_getuservalue"]
pub fn lua_getuservalue_(L: *mut lua_State, idx: c_int);
//
// Set functions (stack -> Lua)
//
pub fn lua_setglobal(L: *mut lua_State, var: *const c_char);
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawseti"]
pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setuservalue(L: *mut lua_State, idx: c_int);
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_callk(L: *mut lua_State, nargs: c_int, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>);
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
errfunc: c_int,
ctx: c_int,
k: Option<lua_CFunction>,
) -> c_int;
pub fn lua_getctx(L: *mut lua_State, ctx: *mut c_int) -> c_int;
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
data: *mut c_void,
chunkname: *const c_char,
mode: *const c_char,
) -> c_int;
#[link_name = "lua_dump"]
pub fn lua_dump_(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int;
}
#[inline(always)]
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
lua_callk(L, n, r, 0, None)
}
#[inline(always)]
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
lua_pcallk(L, n, r, f, 0, None)
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Coroutine functions
//
pub fn lua_yieldk(L: *mut lua_State, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>) -> c_int;
#[link_name = "lua_resume"]
pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
}
#[inline(always)]
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
lua_yieldk(L, n, 0, None)
}
//
// Garbage-collection function and options
//
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
pub const LUA_GCSETMAJORINC: c_int = 8;
pub const LUA_GCISRUNNING: c_int = 9;
pub const LUA_GCGEN: c_int = 10;
pub const LUA_GCINC: c_int = 11;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Miscellaneous functions
//
#[link_name = "lua_error"]
fn lua_error_(L: *mut lua_State) -> c_int;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
pub fn lua_len(L: *mut lua_State, idx: c_int);
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
}
// lua_error does not return but is declared to return int, and Rust translates
// ! to void which can cause link-time errors if the platform linker is aware
// of return types and requires they match (for example: wasm does this).
#[inline(always)]
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
lua_error_(L);
unreachable!();
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tointeger_(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx_(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tounsigned(L: *mut lua_State, i: c_int) -> lua_Unsigned {
lua_tounsignedx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
lua_pushcfunction(L, f);
lua_setglobal(L, n)
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosure(L, f, 0)
}
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= 0) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
lua_pushstring(L, s.as_ptr());
}
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
lua_rawgeti_(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS as _)
}
#[inline(always)]
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
if lua_islightuserdata(L, idx) != 0 {
return lua_touserdata(L, idx);
}
ptr::null_mut()
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
// Maximum size for the description of the source of a function in debug information.
const LUA_IDSIZE: usize = 60;
// Event codes
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
// Event masks
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
}
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub currentline: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub nups: c_uchar,
pub nparams: c_uchar,
pub isvararg: c_char,
pub istailcall: c_char,
pub short_src: [c_char; LUA_IDSIZE],
// lua.h mentions this is for private use
i_ci: *mut c_void,
}
+32
View File
@@ -0,0 +1,32 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_int};
use super::lua::lua_State;
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit32");
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+11
View File
@@ -0,0 +1,11 @@
//! Low level bindings to Lua 5.2.
pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use lualib::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod lualib;
+34
View File
@@ -0,0 +1,34 @@
//! MLua compatibility layer for Lua 5.3
use std::os::raw::{c_char, c_int};
use super::lauxlib::*;
use super::lua::*;
#[inline(always)]
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, from, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
}
ret
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setupvalue(L, -2, 1);
}
status
}
+178
View File
@@ -0,0 +1,178 @@
//! Contains definitions from `lauxlib.h`.
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
// Extra error code for 'luaL_loadfilex'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
// Key, in the registry, for table of loaded modules
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
// Key, in the registry, for table of preloaded loaders
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[link_name = "luaL_tolstring"]
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
-> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;
pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, arg: c_int);
pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
pub fn luaL_checkoption(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
}
// Pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}
#[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
p: *const c_char,
r: *const c_char,
) -> *const c_char;
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}
//
// Some useful macros (implemented as Rust functions)
//
// TODO: luaL_newlibtable, luaL_newlib
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, arg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
lua::lua_typename(L, lua::lua_type(L, i))
}
#[inline(always)]
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
let status = luaL_loadfile(L, filename);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
let status = luaL_loadstring(L, s);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
}
#[inline(always)]
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
}
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
//
// TODO: Generic Buffer Manipulation
//
+514
View File
@@ -0,0 +1,514 @@
//! Contains definitions from `lua.h`.
use std::ffi::CStr;
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_void};
use std::{mem, ptr};
// Mark for precompiled code (`<esc>Lua`)
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
// Option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
// Size of the Lua stack
pub const LUAI_MAXSTACK: c_int = 1000000;
// Size of a raw memory area associated with a Lua state with very fast access.
pub const LUA_EXTRASPACE: usize = mem::size_of::<*const ()>();
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXSTACK - 1000;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i
}
//
// Thread status
//
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRGCMM: c_int = 5;
pub const LUA_ERRERR: c_int = 6;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
pub const LUA_NUMTAGS: c_int = 9;
/// Minimum Lua stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
// Predefined values in the registry
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1;
pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
/// A Lua number, usually equivalent to `f64`
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = i64;
/// A Lua unsigned integer, usually equivalent to `u64`
pub type lua_Unsigned = u64;
/// Type for continuation-function contexts
pub type lua_KContext = isize;
/// Type for native C functions that can be passed to Lua
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
/// Type for continuation functions
pub type lua_KFunction =
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions (no unwinding)
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
//
// State manipulation
//
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
//
// Basic stack manipulation
//
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
//
// Access functions (stack -> C)
//
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
}
//
// Comparison and arithmetic functions
//
pub const LUA_OPADD: c_int = 0;
pub const LUA_OPSUB: c_int = 1;
pub const LUA_OPMUL: c_int = 2;
pub const LUA_OPMOD: c_int = 3;
pub const LUA_OPPOW: c_int = 4;
pub const LUA_OPDIV: c_int = 5;
pub const LUA_OPIDIV: c_int = 6;
pub const LUA_OPBAND: c_int = 7;
pub const LUA_OPBOR: c_int = 8;
pub const LUA_OPBXOR: c_int = 9;
pub const LUA_OPSHL: c_int = 10;
pub const LUA_OPSHR: c_int = 11;
pub const LUA_OPUNM: c_int = 12;
pub const LUA_OPBNOT: c_int = 13;
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Push functions (C -> stack)
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, len: usize) -> *const c_char;
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
// lua_pushvfstring
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
//
// Get functions (Lua -> stack)
//
pub fn lua_getglobal(L: *mut lua_State, name: *const c_char) -> c_int;
pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int;
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int;
//
// Set functions (stack -> Lua)
//
pub fn lua_setglobal(L: *mut lua_State, name: *const c_char);
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer);
pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setuservalue(L: *mut lua_State, idx: c_int);
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
);
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
errfunc: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
data: *mut c_void,
chunkname: *const c_char,
mode: *const c_char,
) -> c_int;
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
}
#[inline(always)]
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
lua_callk(L, n, r, 0, None)
}
#[inline(always)]
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
lua_pcallk(L, n, r, f, 0, None)
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Coroutine functions
//
pub fn lua_yieldk(
L: *mut lua_State,
nresults: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
#[link_name = "lua_resume"]
pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
}
#[inline(always)]
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
lua_yieldk(L, n, 0, None)
}
//
// Garbage-collection function and options
//
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
pub const LUA_GCISRUNNING: c_int = 9;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Miscellaneous functions
//
#[link_name = "lua_error"]
fn lua_error_(L: *mut lua_State) -> c_int;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
pub fn lua_len(L: *mut lua_State, idx: c_int);
pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize;
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
}
// lua_error does not return but is declared to return int, and Rust translates
// ! to void which can cause link-time errors if the platform linker is aware
// of return types and requires they match (for example: wasm does this).
#[inline(always)]
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
lua_error_(L);
unreachable!();
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
(L as *mut c_char).sub(LUA_EXTRASPACE) as *mut c_void
}
#[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
lua_pushcfunction(L, f);
lua_setglobal(L, n)
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosure(L, f, 0)
}
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= 0) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
lua_pushstring(L, s.as_ptr());
}
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
}
#[inline(always)]
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
if lua_islightuserdata(L, idx) != 0 {
return lua_touserdata(L, idx);
}
ptr::null_mut()
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, 1)
}
#[inline(always)]
pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, -1);
lua_pop(L, 1)
}
#[inline(always)]
pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_copy(L, -1, idx);
lua_pop(L, 1)
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
// Maximum size for the description of the source of a function in debug information.
const LUA_IDSIZE: usize = 60;
// Event codes
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
// Event masks
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
}
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub currentline: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub nups: c_uchar,
pub nparams: c_uchar,
pub isvararg: c_char,
pub istailcall: c_char,
pub short_src: [c_char; LUA_IDSIZE],
// lua.h mentions this is for private use
i_ci: *mut c_void,
}
+34
View File
@@ -0,0 +1,34 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_int};
use super::lua::lua_State;
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit32");
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+11
View File
@@ -0,0 +1,11 @@
//! Low level bindings to Lua 5.3.
pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use lualib::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod lualib;
+193
View File
@@ -0,0 +1,193 @@
//! Contains definitions from `lauxlib.h`.
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
// Extra error code for 'luaL_loadfilex'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
// Key, in the registry, for table of loaded modules
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
// Key, in the registry, for table of preloaded loaders
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
-> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;
pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, arg: c_int);
pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
pub fn luaL_checkoption(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
}
// Pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}
#[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
// TODO: luaL_addgsub
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
p: *const c_char,
r: *const c_char,
) -> *const c_char;
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}
//
// Some useful macros (implemented as Rust functions)
//
// TODO: luaL_newlibtable, luaL_newlib
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, arg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
lua::lua_typename(L, lua::lua_type(L, i))
}
#[inline(always)]
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
let status = luaL_loadfile(L, filename);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
let status = luaL_loadstring(L, s);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
}
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua::lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == lua::LUA_OK && env != 0 {
lua::lua_pushvalue(L, env);
lua::lua_setupvalue(L, -2, 1);
}
status
}
//
// TODO: Generic Buffer Manipulation
//
+559
View File
@@ -0,0 +1,559 @@
//! Contains definitions from `lua.h`.
use std::ffi::CStr;
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, c_void};
use std::{mem, ptr};
// Mark for precompiled code (`<esc>Lua`)
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
// Option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
// Size of the Lua stack
pub const LUAI_MAXSTACK: c_int = 1000000;
// Size of a raw memory area associated with a Lua state with very fast access.
pub const LUA_EXTRASPACE: usize = mem::size_of::<*const ()>();
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXSTACK - 1000;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i
}
//
// Thread status
//
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRERR: c_int = 5;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
pub const LUA_NUMTYPES: c_int = 9;
/// Minimum Lua stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
// Predefined values in the registry
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1;
pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
/// A Lua number, usually equivalent to `f64`
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = i64;
/// A Lua unsigned integer, usually equivalent to `u64`
pub type lua_Unsigned = u64;
/// Type for continuation-function contexts
pub type lua_KContext = isize;
/// Type for native C functions that can be passed to Lua
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
/// Type for continuation functions
pub type lua_KFunction =
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions (no unwinding)
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
/// Type for warning functions
pub type lua_WarnFunction = unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
//
// State manipulation
//
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
// Deprecated in Lua 5.4.6
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
#[cfg(feature = "vendored")]
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
pub fn lua_version(L: *mut lua_State) -> lua_Number;
//
// Basic stack manipulation
//
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
//
// Access functions (stack -> C)
//
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
#[link_name = "lua_rawlen"]
fn lua_rawlen_(L: *mut lua_State, idx: c_int) -> lua_Unsigned;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
}
// lua_rawlen's return type changed from size_t to lua_Unsigned int in Lua 5.4.
// This adapts the crate API to the new Lua ABI.
#[inline(always)]
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
lua_rawlen_(L, idx) as usize
}
//
// Comparison and arithmetic functions
//
pub const LUA_OPADD: c_int = 0;
pub const LUA_OPSUB: c_int = 1;
pub const LUA_OPMUL: c_int = 2;
pub const LUA_OPMOD: c_int = 3;
pub const LUA_OPPOW: c_int = 4;
pub const LUA_OPDIV: c_int = 5;
pub const LUA_OPIDIV: c_int = 6;
pub const LUA_OPBAND: c_int = 7;
pub const LUA_OPBOR: c_int = 8;
pub const LUA_OPBXOR: c_int = 9;
pub const LUA_OPSHL: c_int = 10;
pub const LUA_OPSHR: c_int = 11;
pub const LUA_OPUNM: c_int = 12;
pub const LUA_OPBNOT: c_int = 13;
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_arith(L: *mut lua_State, op: c_int);
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Push functions (C -> stack)
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, len: usize) -> *const c_char;
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
// lua_pushvfstring
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
//
// Get functions (Lua -> stack)
//
pub fn lua_getglobal(L: *mut lua_State, name: *const c_char) -> c_int;
pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int;
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_newuserdatauv(L: *mut lua_State, sz: usize, nuvalue: c_int) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_getiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
//
// Set functions (stack -> Lua)
//
pub fn lua_setglobal(L: *mut lua_State, name: *const c_char);
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer);
pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
);
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
errfunc: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
data: *mut c_void,
chunkname: *const c_char,
mode: *const c_char,
) -> c_int;
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
}
#[inline(always)]
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
lua_callk(L, n, r, 0, None)
}
#[inline(always)]
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
lua_pcallk(L, n, r, f, 0, None)
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Coroutine functions
//
pub fn lua_yieldk(
L: *mut lua_State,
nresults: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
}
#[inline(always)]
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
lua_yieldk(L, n, 0, None)
}
//
// Warning-related functions
//
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_setwarnf(L: *mut lua_State, f: Option<lua_WarnFunction>, ud: *mut c_void);
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
}
//
// Garbage-collection function and options
//
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
pub const LUA_GCISRUNNING: c_int = 9;
pub const LUA_GCGEN: c_int = 10;
pub const LUA_GCINC: c_int = 11;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
//
// Miscellaneous functions
//
#[link_name = "lua_error"]
fn lua_error_(L: *mut lua_State) -> c_int;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
pub fn lua_len(L: *mut lua_State, idx: c_int);
pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize;
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
pub fn lua_toclose(L: *mut lua_State, idx: c_int);
pub fn lua_closeslot(L: *mut lua_State, idx: c_int);
}
// lua_error does not return but is declared to return int, and Rust translates
// ! to void which can cause link-time errors if the platform linker is aware
// of return types and requires they match (for example: wasm does this).
#[inline(always)]
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
lua_error_(L);
unreachable!();
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
(L as *mut c_char).sub(LUA_EXTRASPACE) as *mut c_void
}
#[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
lua_pushcfunction(L, f);
lua_setglobal(L, n)
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosure(L, f, 0)
}
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= 0) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
lua_pushstring(L, s.as_ptr());
}
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
}
#[inline(always)]
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
if lua_islightuserdata(L, idx) != 0 {
return lua_touserdata(L, idx);
}
ptr::null_mut()
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, 1)
}
#[inline(always)]
pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, -1);
lua_pop(L, 1)
}
#[inline(always)]
pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_copy(L, -1, idx);
lua_pop(L, 1)
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
#[inline(always)]
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatauv(L, sz, 1)
}
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getiuservalue(L, idx, 1)
}
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_setiuservalue(L, idx, 1)
}
//
// Debug API
//
// Maximum size for the description of the source of a function in debug information.
const LUA_IDSIZE: usize = 60;
// Event codes
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
// Event masks
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
}
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub srclen: usize,
pub currentline: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub nups: c_uchar,
pub nparams: c_uchar,
pub isvararg: c_char,
pub istailcall: c_char,
pub ftransfer: c_ushort,
pub ntransfer: c_ushort,
pub short_src: [c_char; LUA_IDSIZE],
// lua.h mentions this is for private use
i_ci: *mut c_void,
}
+32
View File
@@ -0,0 +1,32 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_int};
use super::lua::lua_State;
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+9
View File
@@ -0,0 +1,9 @@
//! Low level bindings to Lua 5.4.
pub use lauxlib::*;
pub use lua::*;
pub use lualib::*;
pub mod lauxlib;
pub mod lua;
pub mod lualib;
+556
View File
@@ -0,0 +1,556 @@
//! MLua compatibility layer for Luau.
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
use std::{mem, ptr};
use super::lauxlib::*;
use super::lua::*;
use super::luacode::*;
pub const LUA_RESUMEERROR: c_int = -1;
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
while a < b {
lua_pushvalue(L, a);
lua_pushvalue(L, b);
lua_replace(L, a);
lua_replace(L, b);
a += 1;
b -= 1;
}
}
const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack
const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack
unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int {
if level == 0 || lua_istable(L, -1) == 0 {
return 0; // not found
}
lua_pushnil(L); // start 'next' loop
while lua_next(L, -2) != 0 {
// for each pair in table
if lua_type(L, -2) == LUA_TSTRING {
// ignore non-string keys
if lua_rawequal(L, objidx, -1) != 0 {
// found object?
lua_pop(L, 1); // remove value (but keep name)
return 1;
} else if compat53_findfield(L, objidx, level - 1) != 0 {
// try recursively
lua_remove(L, -2); // remove table (but keep name)
lua_pushliteral(L, c".");
lua_insert(L, -2); // place '.' between the two names
lua_concat(L, 3);
return 1;
}
}
lua_pop(L, 1); // remove value
}
0 // not found
}
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int {
let top = lua_gettop(L);
// push function
lua_getinfo(L, level, cstr!("f"), ar);
lua_pushvalue(L, LUA_GLOBALSINDEX);
if compat53_findfield(L, top + 1, 2) != 0 {
lua_copy(L, -1, top + 1); // move name to proper place
lua_pop(L, 2); // remove pushed values
1
} else {
lua_settop(L, top); // remove function and global table
0
}
}
unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) {
if !(*ar).name.is_null() {
// is there a name?
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
} else if compat53_pushglobalfuncname(L, level, ar) != 0 {
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
lua_remove(L, -2); // remove name
} else {
lua_pushliteral(L, c"?");
}
}
//
// lua ported functions
//
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
}
if n > 0 && n < n_elems {
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
n = n_elems - n;
compat53_reverse(L, idx, idx + n - 1);
compat53_reverse(L, idx + n, idx + n_elems - 1);
compat53_reverse(L, idx, idx + n_elems - 1);
}
}
#[inline(always)]
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
let abs_to = lua_absindex(L, toidx);
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
#[inline(always)]
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
return 1;
}
}
0
}
#[inline(always)]
pub unsafe fn lua_pushinteger(L: *mut lua_State, i: lua_Integer) {
lua_pushnumber(L, i as lua_Number);
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);
let n_int = n as lua_Integer;
if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if !isnum.is_null() {
*isnum = 1;
}
return n_int;
}
if !isnum.is_null() {
*isnum = 0;
}
0
}
#[inline(always)]
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
lua_objlen(L, idx)
}
#[inline(always)]
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
if l == 0 {
lua_pushlstring_(L, cstr!(""), 0);
} else {
lua_pushlstring_(L, s, l);
}
lua_tostring(L, -1)
}
#[inline(always)]
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
lua_pushstring_(L, s);
lua_tostring(L, -1)
}
#[inline(always)]
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_gettable(L, idx)
}
#[inline(always)]
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawgeti_(L, idx, n)
}
#[inline(always)]
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
let abs_i = lua_absindex(L, idx);
lua_pushlightuserdata(L, p as *mut c_void);
lua_rawget(L, abs_i)
}
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, mut idx: c_int) -> c_int {
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushliteral(L, c"__mlua_uservalues");
if lua_rawget(L, LUA_REGISTRYINDEX) != LUA_TTABLE {
return LUA_TNIL;
}
lua_pushvalue(L, idx);
lua_rawget(L, -2);
lua_remove(L, -2);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_insert(L, -2);
lua_settable(L, idx);
}
#[inline(always)]
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawseti_(L, idx, n)
}
#[inline(always)]
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
}
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, mut idx: c_int) {
luaL_checkstack(L, 4, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushliteral(L, c"__mlua_uservalues");
lua_pushvalue(L, -1);
if lua_rawget(L, LUA_REGISTRYINDEX) != LUA_TTABLE {
lua_pop(L, 1);
lua_createtable(L, 0, 2); // main table
lua_createtable(L, 0, 1); // metatable
lua_pushliteral(L, c"k");
lua_setfield(L, -2, cstr!("__mode"));
lua_setmetatable(L, -2);
lua_pushvalue(L, -2);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
lua_replace(L, -2);
lua_pushvalue(L, idx);
lua_pushvalue(L, -3);
lua_remove(L, -4);
lua_rawset(L, -3);
lua_pop(L, 1);
}
#[inline(always)]
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
match lua_type(L, idx) {
LUA_TSTRING => {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
LUA_TTABLE => {
if luaL_callmeta(L, idx, cstr!("__len")) == 0 {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
}
LUA_TUSERDATA if luaL_callmeta(L, idx, cstr!("__len")) != 0 => {}
_ => {
luaL_error(
L,
cstr!("attempt to get length of a %s value"),
lua_typename(L, lua_type(L, idx)),
);
}
}
}
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
lua_pushvalue(L, LUA_GLOBALSINDEX);
}
#[inline(always)]
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, from, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
}
ret
}
#[inline(always)]
pub unsafe fn lua_resumex(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = if narg == LUA_RESUMEERROR {
lua_resumeerror(L, from)
} else {
lua_resume_(L, from, narg)
};
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
}
ret
}
//
// lauxlib ported functions
//
#[inline(always)]
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
if lua_checkstack(L, sz + LUA_MINSTACK) == 0 {
if !msg.is_null() {
luaL_error(L, cstr!("stack overflow (%s)"), msg);
} else {
lua_pushliteral(L, c"stack overflow");
lua_error(L);
}
}
}
#[inline(always)]
pub unsafe fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer {
let mut isnum = 0;
let int = lua_tointegerx(L, narg, &mut isnum);
if isnum == 0 {
luaL_typeerror(L, narg, lua_typename(L, LUA_TNUMBER));
}
int
}
pub unsafe fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer {
if lua_isnoneornil(L, narg) != 0 {
def
} else {
luaL_checkinteger(L, narg)
}
}
#[inline(always)]
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
if luaL_getmetafield_(L, obj, e) != 0 {
lua_type(L, -1)
} else {
LUA_TNIL
}
}
#[inline(always)]
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
if luaL_newmetatable_(L, tname) != 0 {
lua_pushstring(L, tname);
lua_setfield(L, -2, cstr!("__type"));
1
} else {
0
}
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
mut size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
extern "C" {
fn free(p: *mut c_void);
}
unsafe extern "C" fn data_dtor(_: *mut lua_State, data: *mut c_void) {
free(*(data as *mut *mut c_char) as *mut c_void);
}
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
if !mode.is_null() {
let modeb = CStr::from_ptr(mode).to_bytes();
if !chunk_is_text && !modeb.contains(&b'b') {
lua_pushfstring(L, cstr!("attempt to load a binary chunk (mode is '%s')"), mode);
return LUA_ERRSYNTAX;
} else if chunk_is_text && !modeb.contains(&b't') {
lua_pushfstring(L, cstr!("attempt to load a text chunk (mode is '%s')"), mode);
return LUA_ERRSYNTAX;
}
}
if chunk_is_text {
if env < 0 {
env -= 1;
}
let data_ud = lua_newuserdatadtor(L, mem::size_of::<*mut c_char>(), data_dtor) as *mut *mut c_char;
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
ptr::write(data_ud, data);
// By deferring the `free(data)` to the userdata destructor, we ensure that
// even if `luau_load` throws an error, the `data` is still released.
let ok = luau_load(L, name, data, size, env) == 0;
lua_replace(L, -2); // replace data with the result
if !ok {
return LUA_ERRSYNTAX;
}
} else if luau_load(L, name, data, size, env) != 0 {
return LUA_ERRSYNTAX;
}
LUA_OK
}
#[inline(always)]
pub unsafe fn luaL_loadbufferx(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int {
luaL_loadbufferenv(L, data, size, name, mode, 0)
}
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
) -> c_int {
luaL_loadbufferenv(L, data, size, name, ptr::null(), 0)
}
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
if isnum == 0 {
luaL_error(L, cstr!("object length is not an integer"));
}
res
}
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
let mut ar: lua_Debug = mem::zeroed();
let top = lua_gettop(L);
let numlevels = lua_stackdepth(L);
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
COMPAT53_LEVELS1
} else {
0
};
if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg);
}
lua_pushliteral(L, c"stack traceback:");
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
if level + 1 == mark {
// too many levels?
lua_pushliteral(L, c"\n\t..."); // add a '...'
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
} else {
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src);
if ar.currentline > 0 {
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
}
lua_pushliteral(L, c" in ");
compat53_pushfuncname(L, level, &mut ar);
lua_concat(L, lua_gettop(L) - top);
}
level += 1;
}
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
LUA_TNIL => {
lua_pushliteral(L, c"nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
LUA_TBOOLEAN => {
if lua_toboolean(L, idx) == 0 {
lua_pushliteral(L, c"false");
} else {
lua_pushliteral(L, c"true");
}
}
t => {
let tt = luaL_getmetafield(L, idx, cstr!("__type"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
} else {
lua_typename(L, t)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__type'
}
}
};
} else if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
lua_tolstring(L, -1, len)
}
#[inline(always)]
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
}
lua_pop(L, 1);
lua_newtable(L);
lua_pushstring_(L, fname);
lua_pushvalue(L, -2);
lua_settable(L, abs_i);
0
}
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
if lua_getfield(L, -1, modname) == LUA_TNIL {
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);
lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
}
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
+211
View File
@@ -0,0 +1,211 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_float, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
extern "C-unwind" {
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[link_name = "luaL_typeerrorL"]
pub fn luaL_typeerror(L: *mut lua_State, narg: c_int, tname: *const c_char) -> !;
#[link_name = "luaL_argerrorL"]
pub fn luaL_argerror(L: *mut lua_State, narg: c_int, extramsg: *const c_char) -> !;
pub fn luaL_checklstring(L: *mut lua_State, narg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
narg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, narg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, narg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkboolean(L: *mut lua_State, narg: c_int) -> c_int;
pub fn luaL_optboolean(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
#[link_name = "luaL_checkinteger"]
pub fn luaL_checkinteger_(L: *mut lua_State, narg: c_int) -> c_int;
#[link_name = "luaL_optinteger"]
pub fn luaL_optinteger_(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned;
pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned;
pub fn luaL_checkvector(L: *mut lua_State, narg: c_int) -> *const c_float;
pub fn luaL_optvector(L: *mut lua_State, narg: c_int, def: *const c_float) -> *const c_float;
#[link_name = "luaL_checkstack"]
pub fn luaL_checkstack_(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, narg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, narg: c_int);
#[link_name = "luaL_newmetatable"]
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_checkbuffer(L: *mut lua_State, narg: c_int, len: *mut usize) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
#[link_name = "luaL_errorL"]
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
pub fn luaL_checkoption(
L: *mut lua_State,
narg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
#[link_name = "luaL_tolstring"]
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_newstate() -> *mut lua_State;
pub fn luaL_findtable(
L: *mut lua_State,
idx: c_int,
fname: *const c_char,
szhint: c_int,
) -> *const c_char;
pub fn luaL_typename(L: *mut lua_State, idx: c_int) -> *const c_char;
pub fn luaL_callyieldable(L: *mut lua_State, nargs: c_int, nresults: c_int) -> c_int;
// sandbox libraries and globals
#[link_name = "luaL_sandbox"]
pub fn luaL_sandbox_(L: *mut lua_State);
pub fn luaL_sandboxthread(L: *mut lua_State);
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, arg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_argexpected(L: *mut lua_State, cond: c_int, arg: c_int, tname: *const c_char) {
if cond == 0 {
luaL_typeerror(L, arg, tname);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
// TODO: luaL_opt
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) -> c_int {
lua::lua_getfield(L, LUA_REGISTRYINDEX, n)
}
#[inline(always)]
pub unsafe fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int {
assert_eq!(t, LUA_REGISTRYINDEX);
let r = lua::lua_ref(L, -1);
lua::lua_pop(L, 1);
r
}
#[inline(always)]
pub unsafe fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int) {
assert_eq!(t, LUA_REGISTRYINDEX);
lua::lua_unref(L, r#ref)
}
pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
use super::lua::*;
// set all libraries to read-only
lua_pushnil(L);
while lua_next(L, LUA_GLOBALSINDEX) != 0 {
if lua_istable(L, -1) != 0 {
lua_setreadonly(L, -1, enabled);
}
lua_pop(L, 1);
}
// set all builtin metatables to read-only
lua_pushliteral(L, c"");
if lua_getmetatable(L, -1) != 0 {
lua_setreadonly(L, -1, enabled);
lua_pop(L, 2);
} else {
lua_pop(L, 1);
}
// set globals to readonly and activate safeenv since the env is immutable
lua_setreadonly(L, LUA_GLOBALSINDEX, enabled);
lua_setsafeenv(L, LUA_GLOBALSINDEX, enabled);
}
//
// Generic Buffer Manipulation
//
/// Buffer size used for on-stack string operations. This limit depends on native stack size.
pub const LUA_BUFFERSIZE: usize = 512;
#[repr(C)]
pub struct luaL_Strbuf {
p: *mut c_char, // current position in buffer
end: *mut c_char, // end of the current buffer
L: *mut lua_State,
storage: *mut c_void, // TString
buffer: [c_char; LUA_BUFFERSIZE],
}
// For compatibility
pub type luaL_Buffer = luaL_Strbuf;
extern "C-unwind" {
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Strbuf);
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
pub fn luaL_prepbuffsize(B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
pub fn luaL_addlstring(B: *mut luaL_Strbuf, s: *const c_char, l: usize);
pub fn luaL_addvalue(B: *mut luaL_Strbuf);
pub fn luaL_addvalueany(B: *mut luaL_Strbuf, idx: c_int);
pub fn luaL_pushresult(B: *mut luaL_Strbuf);
pub fn luaL_pushresultsize(B: *mut luaL_Strbuf, size: usize);
}
pub unsafe fn luaL_addchar(B: *mut luaL_Strbuf, c: c_char) {
if (*B).p >= (*B).end {
luaL_prepbuffsize(B, 1);
}
*(*B).p = c;
(*B).p = (*B).p.add(1);
}
pub unsafe fn luaL_addstring(B: *mut luaL_Strbuf, s: *const c_char) {
// Calculate length of s
let mut len = 0;
while *s.add(len) != 0 {
len += 1;
}
luaL_addlstring(B, s, len);
}
+547
View File
@@ -0,0 +1,547 @@
//! Contains definitions from `lua.h`.
use std::ffi::CStr;
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
use std::{mem, ptr};
// Option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
// Max number of Lua stack slots
const LUAI_MAXCSTACK: c_int = 1000000;
// Number of valid Lua userdata tags
const LUA_UTAG_LIMIT: c_int = 128;
// Number of valid Lua lightuserdata tags
const LUA_LUTAG_LIMIT: c_int = 128;
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXCSTACK - 2000;
pub const LUA_ENVIRONINDEX: c_int = -LUAI_MAXCSTACK - 2001;
pub const LUA_GLOBALSINDEX: c_int = -LUAI_MAXCSTACK - 2002;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_GLOBALSINDEX - i
}
//
// Thread status
//
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRERR: c_int = 5;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TVECTOR: c_int = 4;
pub const LUA_TSTRING: c_int = 5;
pub const LUA_TTABLE: c_int = 6;
pub const LUA_TFUNCTION: c_int = 7;
pub const LUA_TUSERDATA: c_int = 8;
pub const LUA_TTHREAD: c_int = 9;
pub const LUA_TBUFFER: c_int = 10;
/// Guaranteed number of Lua stack slots available to a C function.
pub const LUA_MINSTACK: c_int = 20;
/// A Lua number, usually equivalent to `f64`.
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// A Lua unsigned integer, equivalent to `u32`.
pub type lua_Unsigned = c_uint;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
pub type lua_Continuation = unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int) -> c_int;
/// Type for userdata destructor functions (no unwinding).
pub type lua_Destructor = unsafe extern "C" fn(L: *mut lua_State, *mut c_void);
/// Type for memory-allocation functions (no unwinding).
pub type lua_Alloc =
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
/// Returns Luau release version (eg. `0.xxx`).
pub const fn luau_version() -> Option<&'static str> {
option_env!("LUAU_VERSION")
}
extern "C-unwind" {
//
// State manipulation
//
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_mainthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_resetthread(L: *mut lua_State);
pub fn lua_isthreadreset(L: *mut lua_State) -> c_int;
//
// Basic stack manipulation
//
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_remove(L: *mut lua_State, idx: c_int);
pub fn lua_insert(L: *mut lua_State, idx: c_int);
pub fn lua_replace(L: *mut lua_State, idx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_rawcheckstack(L: *mut lua_State, sz: c_int);
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
pub fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int);
//
// Access functions (stack -> C)
//
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isLfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_equal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
#[link_name = "lua_tointegerx"]
pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> c_int;
pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned;
pub fn lua_tovector(L: *mut lua_State, idx: c_int) -> *const c_float;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn lua_tostringatom(L: *mut lua_State, idx: c_int, atom: *mut c_int) -> *const c_char;
pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char;
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tolightuserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_lightuserdatatag(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_tobuffer(L: *mut lua_State, idx: c_int, len: *mut usize) -> *mut c_void;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
//
// Push functions (C -> stack)
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
#[link_name = "lua_pushinteger"]
pub fn lua_pushinteger_(L: *mut lua_State, n: c_int);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
#[cfg(not(feature = "luau-vector4"))]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
#[cfg(feature = "luau-vector4")]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float, w: c_float);
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
#[link_name = "lua_pushstring"]
pub fn lua_pushstring_(L: *mut lua_State, s: *const c_char);
// lua_pushvfstring
#[link_name = "lua_pushfstringL"]
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosurek(
L: *mut lua_State,
f: lua_CFunction,
debugname: *const c_char,
nup: c_int,
cont: Option<lua_Continuation>,
);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
pub fn lua_newuserdatataggedwithmetatable(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Destructor) -> *mut c_void;
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
//
// Get functions (Lua -> stack)
//
pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
pub fn lua_rawgetfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
#[link_name = "lua_rawgeti"]
pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_setreadonly(L: *mut lua_State, idx: c_int, enabled: c_int);
pub fn lua_getreadonly(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_setsafeenv(L: *mut lua_State, idx: c_int, enabled: c_int);
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_getfenv(L: *mut lua_State, idx: c_int);
//
// Set functions (stack -> Lua)
//
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawseti"]
pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
//
// `load' and `call' functions (load and run Luau bytecode)
//
pub fn luau_load(
L: *mut lua_State,
chunkname: *const c_char,
data: *const c_char,
size: usize,
env: c_int,
) -> c_int;
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
//
// Coroutine functions
//
pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int;
pub fn lua_break(L: *mut lua_State) -> c_int;
#[link_name = "lua_resume"]
pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_resumeerror(L: *mut lua_State, from: *mut lua_State) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
pub fn lua_getthreaddata(L: *mut lua_State) -> *mut c_void;
pub fn lua_setthreaddata(L: *mut lua_State, data: *mut c_void);
}
//
// Garbage-collection function and options
//
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCISRUNNING: c_int = 5;
pub const LUA_GCSTEP: c_int = 6;
pub const LUA_GCSETGOAL: c_int = 7;
pub const LUA_GCSETSTEPMUL: c_int = 8;
pub const LUA_GCSETSTEPSIZE: c_int = 9;
extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
//
// Memory statistics
//
extern "C-unwind" {
pub fn lua_setmemcat(L: *mut lua_State, category: c_int);
pub fn lua_totalbytes(L: *mut lua_State, category: c_int) -> usize;
}
//
// Miscellaneous functions
//
extern "C-unwind" {
pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
// TODO: lua_encodepointer
pub fn lua_clock() -> c_double;
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
pub fn lua_setuserdatametatable(L: *mut lua_State, tag: c_int);
pub fn lua_getuserdatametatable(L: *mut lua_State, tag: c_int);
pub fn lua_setlightuserdataname(L: *mut lua_State, tag: c_int, name: *const c_char);
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
}
//
// Reference system, can be used to pin objects
//
pub const LUA_NOREF: c_int = -1;
pub const LUA_REFNIL: c_int = 0;
extern "C-unwind" {
pub fn lua_ref(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_unref(L: *mut lua_State, r#ref: c_int);
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number {
lua_tonumberx(L, idx, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tointeger_(L: *mut lua_State, idx: c_int) -> c_int {
lua_tointegerx_(L, idx, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tounsigned(L: *mut lua_State, i: c_int) -> lua_Unsigned {
lua_tounsignedx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatatagged(L, sz, 0)
}
#[inline(always)]
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State, data: T) -> *mut T {
unsafe extern "C" fn destructor<T>(_: *mut lua_State, ud: *mut c_void) {
ptr::drop_in_place(ud as *mut T);
}
let ud_ptr = lua_newuserdatadtor(L, const { mem::size_of::<T>() }, destructor::<T>) as *mut T;
ptr::write(ud_ptr, data);
ud_ptr
}
// TODO: lua_strlen
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isvector(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TVECTOR) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isbuffer(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBUFFER) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
lua_pushstring_(L, s.as_ptr());
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosurek(L, f, ptr::null(), 0, None)
}
#[inline(always)]
pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) {
lua_pushcclosurek(L, f, debugname, 0, None)
}
#[inline(always)]
pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) {
lua_pushcclosurek(L, f, ptr::null(), nup, None)
}
#[inline(always)]
pub unsafe fn lua_pushcclosured(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char, nup: c_int) {
lua_pushcclosurek(L, f, debugname, nup, None)
}
#[inline(always)]
pub unsafe fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void) {
lua_pushlightuserdatatagged(L, p, 0)
}
#[inline(always)]
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
lua_setfield(L, LUA_GLOBALSINDEX, var)
}
#[inline(always)]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getfield(L, LUA_GLOBALSINDEX, var)
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
//
// Debug API
//
// Maximum size for the description of the source of a function in debug information.
const LUA_IDSIZE: usize = 256;
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Coverage = unsafe extern "C-unwind" fn(
context: *mut c_void,
function: *const c_char,
linedefined: c_int,
depth: c_int,
hits: *const c_int,
size: usize,
);
extern "C-unwind" {
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getargument(L: *mut lua_State, level: c_int, n: c_int) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int) -> c_int;
pub fn lua_getcoverage(L: *mut lua_State, funcindex: c_int, context: *mut c_void, callback: lua_Coverage);
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
}
#[repr(C)]
pub struct lua_Debug {
pub name: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub short_src: *const c_char,
pub linedefined: c_int,
pub currentline: c_int,
pub nupvals: u8,
pub nparams: u8,
pub isvararg: c_char,
pub userdata: *mut c_void,
pub ssbuf: [c_char; LUA_IDSIZE],
}
//
// Callbacks that can be used to reconfigure behavior of the VM dynamically.
// These are shared between all coroutines.
//
#[repr(C)]
#[non_exhaustive]
pub struct lua_Callbacks {
/// arbitrary userdata pointer that is never overwritten by Luau
pub userdata: *mut c_void,
/// gets called at safepoints (loop back edges, call/ret, gc) if set
pub interrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, gc: c_int)>,
/// gets called when an unprotected error is raised (if longjmp is used)
pub panic: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, errcode: c_int)>,
/// gets called when L is created (LP == parent) or destroyed (LP == NULL)
pub userthread: Option<unsafe extern "C-unwind" fn(LP: *mut lua_State, L: *mut lua_State)>,
/// gets called when a string is created; returned atom can be retrieved via tostringatom
pub useratom: Option<unsafe extern "C-unwind" fn(s: *const c_char, l: usize) -> i16>,
/// gets called when BREAK instruction is encountered
pub debugbreak: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called after each instruction in single step mode
pub debugstep: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when thread execution is interrupted by break in another thread
pub debuginterrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when protected call results in an error
pub debugprotectederror: Option<unsafe extern "C-unwind" fn(L: *mut lua_State)>,
/// gets called when memory is allocated
pub onallocate: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, osize: usize, nsize: usize)>,
}
extern "C" {
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
}
// Functions from customization lib
extern "C" {
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
}
+113
View File
@@ -0,0 +1,113 @@
//! Contains definitions from `luacode.h`.
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_int, c_void};
use std::{ptr, slice};
#[repr(C)]
#[non_exhaustive]
pub struct lua_CompileOptions {
pub optimizationLevel: c_int,
pub debugLevel: c_int,
pub typeInfoLevel: c_int,
pub coverageLevel: c_int,
pub vectorLib: *const c_char,
pub vectorCtor: *const c_char,
pub vectorType: *const c_char,
pub mutableGlobals: *const *const c_char,
pub userdataTypes: *const *const c_char,
pub librariesWithKnownMembers: *const *const c_char,
pub libraryMemberTypeCallback: Option<lua_LibraryMemberTypeCallback>,
pub libraryMemberConstantCallback: Option<lua_LibraryMemberConstantCallback>,
pub disabledBuiltins: *const *const c_char,
}
impl Default for lua_CompileOptions {
fn default() -> Self {
Self {
optimizationLevel: 1,
debugLevel: 1,
typeInfoLevel: 0,
coverageLevel: 0,
vectorLib: ptr::null(),
vectorCtor: ptr::null(),
vectorType: ptr::null(),
mutableGlobals: ptr::null(),
userdataTypes: ptr::null(),
librariesWithKnownMembers: ptr::null(),
libraryMemberTypeCallback: None,
libraryMemberConstantCallback: None,
disabledBuiltins: ptr::null(),
}
}
}
#[repr(C)]
pub struct lua_CompileConstant {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
/// Type table tags
#[doc(hidden)]
#[repr(i32)]
#[non_exhaustive]
pub enum luau_BytecodeType {
Nil = 0,
Boolean,
Number,
String,
Table,
Function,
Thread,
UserData,
Vector,
Buffer,
Any = 15,
}
pub type lua_LibraryMemberTypeCallback =
unsafe extern "C-unwind" fn(library: *const c_char, member: *const c_char) -> c_int;
pub type lua_LibraryMemberConstantCallback = unsafe extern "C-unwind" fn(
library: *const c_char,
member: *const c_char,
constant: *mut lua_CompileConstant,
);
extern "C" {
pub fn luau_set_compile_constant_nil(cons: *mut lua_CompileConstant);
pub fn luau_set_compile_constant_boolean(cons: *mut lua_CompileConstant, b: c_int);
pub fn luau_set_compile_constant_number(cons: *mut lua_CompileConstant, n: f64);
pub fn luau_set_compile_constant_vector(cons: *mut lua_CompileConstant, x: f32, y: f32, z: f32, w: f32);
pub fn luau_set_compile_constant_string(cons: *mut lua_CompileConstant, s: *const c_char, l: usize);
}
extern "C-unwind" {
#[link_name = "luau_compile"]
pub fn luau_compile_(
source: *const c_char,
size: usize,
options: *mut lua_CompileOptions,
outsize: *mut usize,
) -> *mut c_char;
}
extern "C" {
fn free(p: *mut c_void);
}
pub unsafe fn luau_compile(source: &[u8], mut options: lua_CompileOptions) -> Vec<u8> {
let mut outsize = 0;
let data_ptr = luau_compile_(
source.as_ptr() as *const c_char,
source.len(),
&mut options,
&mut outsize,
);
assert!(!data_ptr.is_null(), "luau_compile failed");
let data = slice::from_raw_parts(data_ptr as *mut u8, outsize).to_vec();
free(data_ptr as *mut c_void);
data
}
+11
View File
@@ -0,0 +1,11 @@
//! Contains definitions from `luacodegen.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
extern "C-unwind" {
pub fn luau_codegen_supported() -> c_int;
pub fn luau_codegen_create(state: *mut lua_State);
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
}
+33
View File
@@ -0,0 +1,33 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_int};
use super::lua::lua_State;
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit32");
pub const LUA_BUFFERLIBNAME: *const c_char = cstr!("buffer");
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
pub const LUA_VECLIBNAME: *const c_char = cstr!("vector");
extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
pub fn luaopen_buffer(L: *mut lua_State) -> c_int;
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+154
View File
@@ -0,0 +1,154 @@
//! Contains definitions from `Require.h`.
use std::os::raw::{c_char, c_int, c_void};
use super::lua::lua_State;
pub const LUA_REGISTERED_MODULES_TABLE: *const c_char = cstr!("_REGISTEREDMODULES");
#[repr(C)]
pub enum luarequire_NavigateResult {
Success,
Ambiguous,
NotFound,
}
// Functions returning WriteSuccess are expected to set their size_out argument
// to the number of bytes written to the buffer. If WriteBufferTooSmall is
// returned, size_out should be set to the required buffer size.
#[repr(C)]
pub enum luarequire_WriteResult {
Success,
BufferTooSmall,
Failure,
}
#[repr(C)]
pub struct luarequire_Configuration {
// Returns whether requires are permitted from the given chunkname.
pub is_require_allowed:
unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void, requirer_chunkname: *const c_char) -> bool,
// Resets the internal state to point at the requirer module.
pub reset: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
requirer_chunkname: *const c_char,
) -> luarequire_NavigateResult,
// Resets the internal state to point at an aliased module, given its exact path from a configuration
// file. This function is only called when an alias's path cannot be resolved relative to its
// configuration file.
pub jump_to_alias: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
path: *const c_char,
) -> luarequire_NavigateResult,
// Navigates through the context by making mutations to the internal state.
pub to_parent: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
pub to_child: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
name: *const c_char,
) -> luarequire_NavigateResult,
// Returns whether the context is currently pointing at a module.
pub is_module_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
// Provides the contents of the current module. This function is only called if is_module_present returns
// true.
pub get_contents: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
buffer: *mut c_char,
buffer_size: usize,
size_out: *mut usize,
) -> luarequire_WriteResult,
// Provides a chunkname for the current module. This will be accessible through the debug library. This
// function is only called if is_module_present returns true.
pub get_chunkname: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
buffer: *mut c_char,
buffer_size: usize,
size_out: *mut usize,
) -> luarequire_WriteResult,
// Provides a cache key representing the current module. This function is only called if
// is_module_present returns true.
pub get_cache_key: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
buffer: *mut c_char,
buffer_size: usize,
size_out: *mut usize,
) -> luarequire_WriteResult,
// Returns whether a configuration file is present in the current context.
// If not, require-by-string will call to_parent until either a configuration file is present or
// NAVIGATE_FAILURE is returned (at root).
pub is_config_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
// Provides the contents of the configuration file in the current context.
// This function is only called if is_config_present returns true.
pub get_config: unsafe extern "C" fn(
L: *mut lua_State,
ctx: *mut c_void,
buffer: *mut c_char,
buffer_size: usize,
size_out: *mut usize,
) -> luarequire_WriteResult,
// Executes the module and places the result on the stack. Returns the number of results placed on the
// stack.
// Returning -1 directs the requiring thread to yield. In this case, this thread should be resumed with
// the module result pushed onto its stack.
pub load: unsafe extern "C-unwind" fn(
L: *mut lua_State,
ctx: *mut c_void,
path: *const c_char,
chunkname: *const c_char,
contents: *const c_char,
) -> c_int,
}
// Populates function pointers in the given luarequire_Configuration.
pub type luarequire_Configuration_init = unsafe extern "C" fn(config: *mut luarequire_Configuration);
extern "C-unwind" {
// Initializes and pushes the require closure onto the stack without registration.
pub fn luarequire_pushrequire(
L: *mut lua_State,
config_init: luarequire_Configuration_init,
ctx: *mut c_void,
) -> c_int;
// Initializes the require library and registers it globally.
pub fn luaopen_require(L: *mut lua_State, config_init: luarequire_Configuration_init, ctx: *mut c_void);
// Initializes and pushes a "proxyrequire" closure onto the stack.
//
// The closure takes two parameters: the string path to resolve and the chunkname of an existing
// module.
pub fn luarequire_pushproxyrequire(
L: *mut lua_State,
config_init: luarequire_Configuration_init,
ctx: *mut c_void,
) -> c_int;
// Registers an aliased require path to a result.
//
// After registration, the given result will always be immediately returned when the given path is
// required.
// Expects the path and table to be passed as arguments on the stack.
pub fn luarequire_registermodule(L: *mut lua_State) -> c_int;
// Clears the entry associated with the given cache key from the require cache.
// Expects the cache key to be passed as an argument on the stack.
pub fn luarequire_clearcacheentry(L: *mut lua_State) -> c_int;
// Clears all entries from the require cache.
pub fn luarequire_clearcache(L: *mut lua_State) -> c_int;
}
+17
View File
@@ -0,0 +1,17 @@
//! Low level bindings to Luau.
pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use luacode::*;
pub use luacodegen::*;
pub use lualib::*;
pub use luarequire::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod luacode;
pub mod luacodegen;
pub mod lualib;
pub mod luarequire;
+6
View File
@@ -0,0 +1,6 @@
#[allow(unused_macros)]
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
};
}
+8 -8
View File
@@ -1,10 +1,10 @@
[package]
name = "mlua_derive"
version = "0.6.0"
version = "0.10.1"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
edition = "2021"
description = "Procedural macros for the mlua crate."
repository = "https://github.com/khvzak/mlua"
repository = "https://github.com/mlua-rs/mlua"
keywords = ["lua", "mlua"]
license = "MIT"
@@ -12,13 +12,13 @@ license = "MIT"
proc-macro = true
[features]
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
macros = ["proc-macro-error2", "itertools", "regex", "once_cell"]
[dependencies]
quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error = { version = "1.0", optional = true }
syn = { version = "1.0", features = ["full"] }
itertools = { version = "0.10", optional = true }
proc-macro-error2 = { version = "2.0.1", optional = true }
syn = { version = "2.0", features = ["full"] }
itertools = { version = "0.14", optional = true }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.5", optional = true }
once_cell = { version = "1.0", optional = true }
+31
View File
@@ -0,0 +1,31 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
pub fn from_lua(input: TokenStream) -> TokenStream {
let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
let ident_str = ident.to_string();
let (impl_generics, ty_generics, _) = generics.split_for_impl();
let where_clause = match &generics.where_clause {
Some(where_clause) => quote! { #where_clause, Self: 'static + Clone },
None => quote! { where Self: 'static + Clone },
};
quote! {
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
#[inline]
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
match value {
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => Err(::mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: #ident_str.to_string(),
message: None,
}),
}
}
}
}
.into()
}
+77 -37
View File
@@ -1,38 +1,73 @@
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
use syn::meta::ParseNestedMeta;
use syn::{parse_macro_input, ItemFn, LitStr, Result};
#[cfg(feature = "macros")]
use {
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
proc_macro_error::proc_macro_error,
proc_macro_error2::proc_macro_error,
};
#[derive(Default)]
struct ModuleAttributes {
name: Option<Ident>,
skip_memory_check: bool,
}
impl ModuleAttributes {
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
if meta.path.is_ident("name") {
match meta.value() {
Ok(value) => {
self.name = Some(value.parse::<LitStr>()?.parse()?);
}
Err(_) => {
return Err(meta.error("`name` attribute must have a value"));
}
}
} else if meta.path.is_ident("skip_memory_check") {
if meta.value().is_ok() {
return Err(meta.error("`skip_memory_check` attribute have no values"));
}
self.skip_memory_check = true;
} else {
return Err(meta.error("unsupported module attribute"));
}
Ok(())
}
}
#[proc_macro_attribute]
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as AttributeArgs);
let func = parse_macro_input!(item as ItemFn);
if !args.is_empty() {
let err = Error::new(Span::call_site(), "the macro does not support arguments")
.to_compile_error();
return err.into();
let mut args = ModuleAttributes::default();
if !attr.is_empty() {
let args_parser = syn::meta::parser(|meta| args.parse(meta));
parse_macro_input!(attr with args_parser);
}
let func_name = func.sig.ident.clone();
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
let func = parse_macro_input!(item as ItemFn);
let func_name = &func.sig.ident;
let module_name = args.name.unwrap_or_else(|| func_name.clone());
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
let skip_memory_check = if args.skip_memory_check {
quote! { lua.skip_memory_check(true); }
} else {
quote! {}
};
let wrapped = quote! {
::mlua::require_module_feature!();
mlua::require_module_feature!();
#func
#[no_mangle]
unsafe extern "C" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
::mlua::Lua::init_from_ptr(state)
.entrypoint1(#func_name)
.expect("cannot initialize module")
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
mlua::Lua::entrypoint1(state, move |lua| {
#skip_memory_check
#func_name(lua)
})
}
};
@@ -61,28 +96,21 @@ pub fn chunk(input: TokenStream) -> TokenStream {
});
let wrapped_code = quote! {{
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
use ::std::marker::PhantomData;
use ::std::sync::Mutex;
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
use ::std::borrow::Cow;
use ::std::cell::Cell;
use ::std::io::Result as IoResult;
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
impl<F> AsChunk<'static> for InnerChunk<F>
where
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
F: FnOnce(&Lua) -> Result<Table>,
{
fn source(&self) -> &[u8] {
(#source).as_bytes()
}
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
if #caps_len > 0 {
if let Ok(mut make_env) = self.0.lock() {
if let Some(make_env) = make_env.take() {
return make_env(lua).map(Some);
}
if let Some(make_env) = self.0.take() {
return make_env(lua).map(Some);
}
}
Ok(None)
@@ -91,9 +119,13 @@ pub fn chunk(input: TokenStream) -> TokenStream {
fn mode(&self) -> Option<ChunkMode> {
Some(ChunkMode::Text)
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
}
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
let make_env = move |lua: &Lua| -> Result<Table> {
let globals = lua.globals();
let env = lua.create_table()?;
let meta = lua.create_table()?;
@@ -104,16 +136,24 @@ pub fn chunk(input: TokenStream) -> TokenStream {
#(#caps)*
env.set_metatable(Some(meta));
Ok(Value::Table(env))
});
Ok(env)
};
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
InnerChunk(Cell::new(Some(make_env)))
}};
wrapped_code.into()
}
#[cfg(feature = "macros")]
#[proc_macro_derive(FromLua)]
pub fn from_lua(input: TokenStream) -> TokenStream {
from_lua::from_lua(input)
}
#[cfg(feature = "macros")]
mod chunk;
#[cfg(feature = "macros")]
mod from_lua;
#[cfg(feature = "macros")]
mod token;
+8 -17
View File
@@ -1,9 +1,6 @@
use std::{
cmp::{Eq, PartialEq},
fmt::{self, Display, Formatter},
iter::IntoIterator,
vec::IntoIter,
};
use std::cmp::{Eq, PartialEq};
use std::fmt::{self, Display, Formatter};
use std::vec::IntoIter;
use itertools::Itertools;
use once_cell::sync::Lazy;
@@ -48,10 +45,7 @@ fn span_pos(span: &Span) -> (Pos, Pos) {
return fallback_span_pos(span);
}
(
Pos::new(start.line, start.column),
Pos::new(end.line, end.column),
)
(Pos::new(start.line, start.column), Pos::new(end.line, end.column))
}
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
@@ -59,7 +53,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
match RE.captures(&format!("{:?}", span)) {
match RE.captures(&format!("{span:?}")) {
Some(caps) => match (caps.get(1), caps.get(2)) {
(Some(start), Some(end)) => Some((
match start.as_str().parse() {
@@ -80,9 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
let (start, end) = match parse_pos(span) {
Some(v) => v,
None => proc_macro_error::abort_call_site!(
"Cannot retrieve span information; please use nightly"
),
None => proc_macro_error2::abort_call_site!("Cannot retrieve span information; please use nightly"),
};
(Pos::new(1, start), Pos::new(1, end))
}
@@ -175,8 +167,7 @@ impl Tokens {
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
Tokens(
tt.into_iter()
.map(Tokens::from)
.flatten()
.flat_map(Tokens::from)
.peekable()
.batching(|iter| {
// Find variable tokens
@@ -217,7 +208,7 @@ impl From<TokenTree> for Tokens {
vec![Token::new_delim(b, tt.clone(), true)]
.into_iter()
.chain(g.stream().into_iter().map(Tokens::from).flatten())
.chain(g.stream().into_iter().flat_map(Tokens::from))
.chain(vec![Token::new_delim(e, tt, false)])
.collect()
}
+4
View File
@@ -0,0 +1,4 @@
imports_granularity = "Module"
max_width = 110
comment_width = 100
wrap_comments = true
+86
View File
@@ -0,0 +1,86 @@
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, Serializer};
use crate::types::ValueRef;
/// A Luau buffer type.
///
/// See the buffer [documentation] for more information.
///
/// [documentation]: https://luau.org/library#buffer-library
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq)]
pub struct Buffer(pub(crate) ValueRef);
#[cfg_attr(not(feature = "luau"), allow(unused))]
impl Buffer {
/// Copies the buffer data into a new `Vec<u8>`.
pub fn to_vec(&self) -> Vec<u8> {
unsafe { self.as_slice().to_vec() }
}
/// Returns the length of the buffer.
pub fn len(&self) -> usize {
unsafe { self.as_slice().len() }
}
/// Returns `true` if the buffer is empty.
#[doc(hidden)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Reads given number of bytes from the buffer at the given offset.
///
/// Offset is 0-based.
#[track_caller]
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
let data = unsafe { self.as_slice() };
let mut bytes = [0u8; N];
bytes.copy_from_slice(&data[offset..offset + N]);
bytes
}
/// Writes given bytes to the buffer at the given offset.
///
/// Offset is 0-based.
#[track_caller]
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
let data = unsafe {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts_mut(buf, size)
};
data[offset..offset + bytes.len()].copy_from_slice(bytes);
}
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts(buf, size)
}
#[cfg(feature = "luau")]
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
let lua = self.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
(buf as *mut u8, size)
}
#[cfg(not(feature = "luau"))]
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
unreachable!()
}
}
#[cfg(feature = "serialize")]
impl Serialize for Buffer {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
serializer.serialize_bytes(unsafe { self.as_slice() })
}
}
#[cfg(feature = "luau")]
impl crate::types::LuaType for Buffer {
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
}
+618
View File
@@ -0,0 +1,618 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::CString;
use std::io::Result as IoResult;
use std::marker::PhantomData;
use std::panic::Location;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, WeakLua};
use crate::table::Table;
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::value::Value;
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
///
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
pub trait AsChunk<'a> {
/// Returns optional chunk name
///
/// See [`Chunk::set_name`] for possible name prefixes.
fn name(&self) -> Option<StdString> {
None
}
/// Returns optional chunk [environment]
///
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
let _lua = lua; // suppress warning
Ok(None)
}
/// Returns optional chunk mode (text or binary)
fn mode(&self) -> Option<ChunkMode> {
None
}
/// Returns chunk data (can be text or binary)
fn source(self) -> IoResult<Cow<'a, [u8]>>;
}
impl<'a> AsChunk<'a> for &'a str {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl AsChunk<'static> for StdString {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self.into_bytes()))
}
}
impl<'a> AsChunk<'a> for &'a StdString {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_bytes()))
}
}
impl<'a> AsChunk<'a> for &'a [u8] {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl AsChunk<'static> for Vec<u8> {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self))
}
}
impl<'a> AsChunk<'a> for &'a Vec<u8> {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl AsChunk<'static> for &Path {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
std::fs::read(self).map(Cow::Owned)
}
}
impl AsChunk<'static> for PathBuf {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
std::fs::read(self).map(Cow::Owned)
}
}
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'a> {
pub(crate) lua: WeakLua,
pub(crate) name: StdString,
pub(crate) env: Result<Option<Table>>,
pub(crate) mode: Option<ChunkMode>,
pub(crate) source: IoResult<Cow<'a, [u8]>>,
#[cfg(feature = "luau")]
pub(crate) compiler: Option<Compiler>,
}
/// Represents chunk mode (text or binary).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChunkMode {
Text,
Binary,
}
/// Luau compiler
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug)]
pub struct Compiler {
optimization_level: u8,
debug_level: u8,
type_info_level: u8,
coverage_level: u8,
vector_lib: Option<String>,
vector_ctor: Option<String>,
vector_type: Option<String>,
mutable_globals: Vec<String>,
userdata_types: Vec<String>,
}
#[cfg(any(feature = "luau", doc))]
impl Default for Compiler {
fn default() -> Self {
const { Self::new() }
}
}
#[cfg(any(feature = "luau", doc))]
impl Compiler {
/// Creates Luau compiler instance with default options
pub const fn new() -> Self {
// Defaults are taken from luacode.h
Compiler {
optimization_level: 1,
debug_level: 1,
type_info_level: 0,
coverage_level: 0,
vector_lib: None,
vector_ctor: None,
vector_type: None,
mutable_globals: Vec::new(),
userdata_types: Vec::new(),
}
}
/// Sets Luau compiler optimization level.
///
/// Possible values:
/// * 0 - no optimization
/// * 1 - baseline optimization level that doesn't prevent debuggability (default)
/// * 2 - includes optimizations that harm debuggability such as inlining
#[must_use]
pub const fn set_optimization_level(mut self, level: u8) -> Self {
self.optimization_level = level;
self
}
/// Sets Luau compiler debug level.
///
/// Possible values:
/// * 0 - no debugging support
/// * 1 - line info & function names only; sufficient for backtraces (default)
/// * 2 - full debug info with local & upvalue names; necessary for debugger
#[must_use]
pub const fn set_debug_level(mut self, level: u8) -> Self {
self.debug_level = level;
self
}
/// Sets Luau type information level used to guide native code generation decisions.
///
/// Possible values:
/// * 0 - generate for native modules (default)
/// * 1 - generate for all modules
pub const fn set_type_info_level(mut self, level: u8) -> Self {
self.type_info_level = level;
self
}
/// Sets Luau compiler code coverage level.
///
/// Possible values:
/// * 0 - no code coverage support (default)
/// * 1 - statement coverage
/// * 2 - statement and expression coverage (verbose)
#[must_use]
pub const fn set_coverage_level(mut self, level: u8) -> Self {
self.coverage_level = level;
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
self.vector_lib = Some(lib.into());
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
self.vector_ctor = Some(ctor.into());
self
}
#[doc(hidden)]
#[must_use]
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
self.vector_type = Some(r#type.into());
self
}
/// Sets a list of globals that are mutable.
///
/// It disables the import optimization for fields accessed through these.
#[must_use]
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
self.mutable_globals = globals;
self
}
/// Sets a list of userdata types that will be included in the type information.
#[must_use]
pub fn set_userdata_types(mut self, types: Vec<String>) -> Self {
self.userdata_types = types;
self
}
/// Compiles the `source` into bytecode.
///
/// Returns [`Error::SyntaxError`] if the source code is invalid.
pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>> {
use std::os::raw::c_int;
use std::ptr;
let vector_lib = self.vector_lib.clone();
let vector_lib = vector_lib.and_then(|lib| CString::new(lib).ok());
let vector_lib = vector_lib.as_ref();
let vector_ctor = self.vector_ctor.clone();
let vector_ctor = vector_ctor.and_then(|ctor| CString::new(ctor).ok());
let vector_ctor = vector_ctor.as_ref();
let vector_type = self.vector_type.clone();
let vector_type = vector_type.and_then(|t| CString::new(t).ok());
let vector_type = vector_type.as_ref();
macro_rules! vec2cstring_ptr {
($name:ident, $name_ptr:ident) => {
let $name = self
.$name
.iter()
.map(|name| CString::new(name.clone()).ok())
.collect::<Option<Vec<_>>>()
.unwrap_or_default();
let mut $name = $name.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
let mut $name_ptr = ptr::null();
if !$name.is_empty() {
$name.push(ptr::null());
$name_ptr = $name.as_ptr();
}
};
}
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
let bytecode = unsafe {
let mut options = ffi::lua_CompileOptions::default();
options.optimizationLevel = self.optimization_level as c_int;
options.debugLevel = self.debug_level as c_int;
options.typeInfoLevel = self.type_info_level as c_int;
options.coverageLevel = self.coverage_level as c_int;
options.vectorLib = vector_lib.map_or(ptr::null(), |s| s.as_ptr());
options.vectorCtor = vector_ctor.map_or(ptr::null(), |s| s.as_ptr());
options.vectorType = vector_type.map_or(ptr::null(), |s| s.as_ptr());
options.mutableGlobals = mutable_globals_ptr;
options.userdataTypes = userdata_types_ptr;
ffi::luau_compile(source.as_ref(), options)
};
if bytecode.first() == Some(&0) {
// The rest of the bytecode is the error message starting with `:`
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
let message = String::from_utf8_lossy(&bytecode[2..]).to_string();
return Err(Error::SyntaxError {
incomplete_input: message.ends_with("<eof>"),
message,
});
}
Ok(bytecode)
}
}
impl Chunk<'_> {
/// Returns the name of this chunk.
pub fn name(&self) -> &str {
&self.name
}
/// Sets the name of this chunk, which results in more informative error traces.
///
/// Possible name prefixes:
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
/// more useful for identifying the file)
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
pub fn set_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
/// Returns the environment of this chunk.
pub fn environment(&self) -> Option<&Table> {
self.env.as_ref().ok()?.as_ref()
}
/// Sets the environment of the loaded chunk to the given value.
///
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the
/// `_ENV` variable inside the chunk. By default this value is set to the global environment.
///
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
/// the chunk will refer to the given environment rather than the global one.
///
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
/// necessary to populate the environment in order for scripts using custom environments to be
/// useful.
pub fn set_environment(mut self, env: Table) -> Self {
self.env = Ok(Some(env));
self
}
/// Returns the mode (auto-detected by default) of this chunk.
pub fn mode(&self) -> ChunkMode {
self.detect_mode()
}
/// Sets whether the chunk is text or binary (autodetected by default).
///
/// Be aware, Lua does not check the consistency of the code inside binary chunks.
/// Running maliciously crafted bytecode can crash the interpreter.
pub fn set_mode(mut self, mode: ChunkMode) -> Self {
self.mode = Some(mode);
self
}
/// Sets or overwrites a Luau compiler used for this chunk.
///
/// See [`Compiler`] for details and possible options.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_compiler(mut self, compiler: Compiler) -> Self {
self.compiler = Some(compiler);
self
}
/// Execute this chunk of code.
///
/// This is equivalent to calling the chunk function with no arguments and no return values.
pub fn exec(self) -> Result<()> {
self.call(())
}
/// Asynchronously execute this chunk of code.
///
/// See [`exec`] for more details.
///
/// Requires `feature = "async"`
///
/// [`exec`]: Chunk::exec
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn exec_async(self) -> Result<()> {
self.call_async(()).await
}
/// Evaluate the chunk as either an expression or block.
///
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
/// and this is equivalent to calling `exec`.
pub fn eval<R: FromLuaMulti>(self) -> Result<R> {
// Bytecode is always interpreted as a statement.
// For source code, first try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
// actual lua repl does.
if self.detect_mode() == ChunkMode::Binary {
self.call(())
} else if let Ok(function) = self.to_expression() {
function.call(())
} else {
self.call(())
}
}
/// Asynchronously evaluate the chunk as either an expression or block.
///
/// See [`eval`] for more details.
///
/// Requires `feature = "async"`
///
/// [`eval`]: Chunk::eval
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn eval_async<R>(self) -> Result<R>
where
R: FromLuaMulti,
{
if self.detect_mode() == ChunkMode::Binary {
self.call_async(()).await
} else if let Ok(function) = self.to_expression() {
function.call_async(()).await
} else {
self.call_async(()).await
}
}
/// Load the chunk function and call it with the given arguments.
///
/// This is equivalent to `into_function` and calling the resulting function.
pub fn call<R: FromLuaMulti>(self, args: impl IntoLuaMulti) -> Result<R> {
self.into_function()?.call(args)
}
/// Load the chunk function and asynchronously call it with the given arguments.
///
/// See [`call`] for more details.
///
/// Requires `feature = "async"`
///
/// [`call`]: Chunk::call
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn call_async<R>(self, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti,
{
self.into_function()?.call_async(args).await
}
/// Load this chunk into a regular [`Function`].
///
/// This simply compiles the chunk without actually executing it.
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
pub fn into_function(mut self) -> Result<Function> {
#[cfg(feature = "luau")]
if self.compiler.is_some() {
// We don't need to compile source if no compiler set
self.compile();
}
let name = Self::convert_name(self.name)?;
self.lua
.lock()
.load_chunk(Some(&name), self.env?.as_ref(), self.mode, self.source?.as_ref())
}
/// Compiles the chunk and changes mode to binary.
///
/// It does nothing if the chunk is already binary or invalid.
fn compile(&mut self) {
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
#[cfg(feature = "luau")]
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
#[cfg(not(feature = "luau"))]
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
let data = func.dump(false);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
}
}
}
/// Fetches compiled bytecode of this chunk from the cache.
///
/// If not found, compiles the source code and stores it on the cache.
pub(crate) fn try_cache(mut self) -> Self {
struct ChunksCache(HashMap<Vec<u8>, Vec<u8>>);
// Try to fetch compiled chunk from cache
let mut text_source = None;
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
let lua = self.lua.lock();
if let Some(cache) = lua.app_data_ref_unguarded::<ChunksCache>() {
if let Some(data) = cache.0.get(source.as_ref()) {
self.source = Ok(Cow::Owned(data.clone()));
self.mode = Some(ChunkMode::Binary);
return self;
}
}
text_source = Some(source.as_ref().to_vec());
}
}
// Compile and cache the chunk
if let Some(text_source) = text_source {
self.compile();
if let Ok(ref binary_source) = self.source {
if self.detect_mode() == ChunkMode::Binary {
let lua = self.lua.lock();
if let Some(mut cache) = lua.app_data_mut_unguarded::<ChunksCache>() {
cache.0.insert(text_source, binary_source.as_ref().to_vec());
} else {
let mut cache = ChunksCache(HashMap::new());
cache.0.insert(text_source, binary_source.as_ref().to_vec());
let _ = lua.try_set_app_data(cache);
};
}
}
}
self
}
fn to_expression(&self) -> Result<Function> {
// We assume that mode is Text
let source = self.source.as_ref();
let source = source.map_err(Error::runtime)?;
let source = Self::expression_source(source);
// We don't need to compile source if no compiler options set
#[cfg(feature = "luau")]
let source = self
.compiler
.as_ref()
.map(|c| c.compile(&source))
.transpose()?
.unwrap_or(source);
let name = Self::convert_name(self.name.clone())?;
let env = match &self.env {
Ok(Some(env)) => Some(env),
Ok(None) => None,
Err(err) => return Err(err.clone()),
};
self.lua.lock().load_chunk(Some(&name), env, None, &source)
}
fn detect_mode(&self) -> ChunkMode {
match (self.mode, &self.source) {
(Some(mode), _) => mode,
(None, Ok(source)) => {
#[cfg(not(feature = "luau"))]
if source.starts_with(ffi::LUA_SIGNATURE) {
return ChunkMode::Binary;
}
#[cfg(feature = "luau")]
if *source.first().unwrap_or(&u8::MAX) < b'\n' {
return ChunkMode::Binary;
}
ChunkMode::Text
}
(None, Err(_)) => ChunkMode::Text, // any value is fine
}
}
fn convert_name(name: String) -> Result<CString> {
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
}
fn expression_source(source: &[u8]) -> Vec<u8> {
let mut buf = Vec::with_capacity(b"return ".len() + source.len());
buf.extend(b"return ");
buf.extend(source);
buf
}
}
struct WrappedChunk<'a, T: AsChunk<'a>> {
chunk: T,
caller: &'static Location<'static>,
_marker: PhantomData<&'a T>,
}
impl<'a> Chunk<'a> {
/// Wraps a chunk of Lua code, returning an opaque type that implements [`IntoLua`] trait.
///
/// The resulted `IntoLua` implementation will convert the chunk into a Lua function without
/// executing it.
#[doc(hidden)]
#[track_caller]
pub fn wrap(chunk: impl AsChunk<'a> + 'a) -> impl IntoLua + 'a {
WrappedChunk {
chunk,
caller: Location::caller(),
_marker: PhantomData,
}
}
}
impl<'a, T: AsChunk<'a>> IntoLua for WrappedChunk<'a, T> {
fn into_lua(self, lua: &Lua) -> Result<Value> {
lua.load_with_location(self.chunk, self.caller)
.into_function()
.map(Value::Function)
}
}
+866 -275
View File
File diff suppressed because it is too large Load Diff
+317 -83
View File
@@ -1,5 +1,3 @@
#![allow(clippy::wrong_self_convention)]
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;
@@ -9,6 +7,14 @@ use std::str::Utf8Error;
use std::string::String as StdString;
use std::sync::Arc;
use crate::private::Sealed;
#[cfg(feature = "error-send")]
type DynStdError = dyn StdError + Send + Sync;
#[cfg(not(feature = "error-send"))]
type DynStdError = dyn StdError;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
#[non_exhaustive]
@@ -37,20 +43,16 @@ pub enum Error {
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
///
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
GarbageCollectorError(StdString),
/// Potentially unsafe action in safe mode.
SafetyError(StdString),
/// Setting memory limit is not available.
/// Memory control is not available.
///
/// This error can only happen when Lua state was not created by us and does not have the
/// custom allocator attached.
MemoryLimitNotAvailable,
/// Main thread is not available.
///
/// This error can only happen in Lua5.1/LuaJIT module mode, when module loaded within a coroutine.
/// These Lua versions does not have `LUA_RIDX_MAINTHREAD` registry key.
MainThreadNotAvailable,
MemoryControlNotAvailable,
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
///
/// This is an error because a mutable callback can only be borrowed mutably once.
@@ -65,15 +67,31 @@ pub enum Error {
///
/// Due to the way `mlua` works, it should not be directly possible to run out of stack space
/// during normal use. The only way that this error can be triggered is if a `Function` is
/// called with a huge number of arguments, or a rust callback returns a huge number of return
/// called with a huge number of arguments, or a Rust callback returns a huge number of return
/// values.
StackError,
/// Too many arguments to `Function::bind`
/// Too many arguments to [`Function::bind`].
///
/// [`Function::bind`]: crate::Function::bind
BindError,
/// Bad argument received from Lua (usually when calling a function).
///
/// This error can help to identify the argument that caused the error
/// (which is stored in the corresponding field).
BadArgument {
/// Function that was called.
to: Option<StdString>,
/// Argument position (usually starts from 1).
pos: usize,
/// Argument name.
name: Option<StdString>,
/// Underlying error returned when converting argument to a Lua value.
cause: Arc<Error>,
},
/// A Rust value could not be converted to a Lua value.
ToLuaConversionError {
/// Name of the Rust type that could not be converted.
from: &'static str,
from: String,
/// Name of the Lua type that could not be created.
to: &'static str,
/// A message indicating why the conversion failed in more detail.
@@ -84,68 +102,73 @@ pub enum Error {
/// Name of the Lua type that could not be converted.
from: &'static str,
/// Name of the Rust type that could not be created.
to: &'static str,
to: String,
/// A string containing more detailed error information.
message: Option<StdString>,
},
/// [`Thread::resume`] was called on an inactive coroutine.
/// [`Thread::resume`] was called on an unresumable coroutine.
///
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
/// the coroutine.
/// A coroutine is unresumable if its main function has returned or if an error has occurred
/// inside the coroutine. Already running coroutines are also marked as unresumable.
///
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
/// error.
///
/// [`Thread::resume`]: struct.Thread.html#method.resume
/// [`Thread::status`]: struct.Thread.html#method.status
CoroutineInactive,
/// [`Thread::resume`]: crate::Thread::resume
/// [`Thread::status`]: crate::Thread::status
CoroutineUnresumable,
/// An [`AnyUserData`] is not the expected type in a borrow.
///
/// This error can only happen when manually using [`AnyUserData`], or when implementing
/// metamethods for binary operators. Refer to the documentation of [`UserDataMethods`] for
/// details.
///
/// [`AnyUserData`]: struct.AnyUserData.html
/// [`UserDataMethods`]: trait.UserDataMethods.html
/// [`AnyUserData`]: crate::AnyUserData
/// [`UserDataMethods`]: crate::UserDataMethods
UserDataTypeMismatch,
/// An [`AnyUserData`] borrow failed because it has been destructed.
///
/// This error can happen either due to to being destructed in a previous __gc, or due to being
/// destructed from exiting a `Lua::scope` call.
///
/// [`AnyUserData`]: struct.AnyUserData.html
/// [`AnyUserData`]: crate::AnyUserData
UserDataDestructed,
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
/// An [`AnyUserData`] immutable borrow failed.
///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
/// prevent these errors.
///
/// [`AnyUserData`]: struct.AnyUserData.html
/// [`UserData`]: trait.UserData.html
/// [`AnyUserData`]: crate::AnyUserData
/// [`UserData`]: crate::UserData
UserDataBorrowError,
/// An [`AnyUserData`] mutable borrow failed because it is already borrowed.
/// An [`AnyUserData`] mutable borrow failed.
///
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
/// prevent these errors.
///
/// [`AnyUserData`]: struct.AnyUserData.html
/// [`UserData`]: trait.UserData.html
/// [`AnyUserData`]: crate::AnyUserData
/// [`UserData`]: crate::UserData
UserDataBorrowMutError,
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
///
/// [`MetaMethod`]: enum.MetaMethod.html
/// [`MetaMethod`]: crate::MetaMethod
MetaMethodRestricted(StdString),
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
///
/// [`MetaMethod`]: enum.MetaMethod.html
/// [`MetaMethod`]: crate::MetaMethod
MetaMethodTypeError {
/// Name of the metamethod.
method: StdString,
/// Passed value type.
type_name: &'static str,
/// A string containing more detailed error information.
message: Option<StdString>,
},
/// A `RegistryKey` produced from a different Lua state was used.
/// A [`RegistryKey`] produced from a different Lua state was used.
///
/// [`RegistryKey`]: crate::RegistryKey
MismatchedRegistryKey,
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
CallbackError {
@@ -174,7 +197,14 @@ pub enum Error {
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered.
ExternalError(Arc<dyn StdError + Send + Sync>),
ExternalError(Arc<DynStdError>),
/// An error with additional context.
WithContext {
/// A string containing additional context.
context: StdString,
/// Underlying error.
cause: Arc<Error>,
},
}
/// A specialized `Result` type used by `mlua`'s API.
@@ -183,24 +213,21 @@ pub type Result<T> = StdResult<T, Error>;
#[cfg(not(tarpaulin_include))]
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
Error::MemoryError(ref msg) => {
write!(fmt, "memory error: {}", msg)
match self {
Error::SyntaxError { message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(msg) => write!(fmt, "runtime error: {msg}"),
Error::MemoryError(msg) => {
write!(fmt, "memory error: {msg}")
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {}", msg)
Error::GarbageCollectorError(msg) => {
write!(fmt, "garbage collector error: {msg}")
}
Error::SafetyError(ref msg) => {
write!(fmt, "safety error: {}", msg)
Error::SafetyError(msg) => {
write!(fmt, "safety error: {msg}")
},
Error::MemoryLimitNotAvailable => {
write!(fmt, "setting memory limit is not available")
}
Error::MainThreadNotAvailable => {
write!(fmt, "main thread is not available in Lua 5.1")
Error::MemoryControlNotAvailable => {
write!(fmt, "memory control is not available")
}
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
Error::CallbackDestructed => write!(
@@ -215,107 +242,253 @@ impl fmt::Display for Error {
fmt,
"too many arguments to Function::bind"
),
Error::ToLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting {} to Lua {}", from, to)?;
match *message {
Error::BadArgument { to, pos, name, cause } => {
if let Some(name) = name {
write!(fmt, "bad argument `{name}`")?;
} else {
write!(fmt, "bad argument #{pos}")?;
}
if let Some(to) = to {
write!(fmt, " to `{to}`")?;
}
write!(fmt, ": {cause}")
},
Error::ToLuaConversionError { from, to, message } => {
write!(fmt, "error converting {from} to Lua {to}")?;
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({})", message),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::FromLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting Lua {} to {}", from, to)?;
match *message {
Error::FromLuaConversionError { from, to, message } => {
write!(fmt, "error converting Lua {from} to {to}")?;
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({})", message),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
match *message {
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { method, type_name, message } => {
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({})", message),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref traceback, .. } => {
write!(fmt, "callback error\n{}", traceback)
Error::CallbackError { cause, traceback } => {
// Trace errors down to the root
let (mut cause, mut full_traceback) = (cause, None);
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
cause = cause2;
full_traceback = Some(traceback2);
}
writeln!(fmt, "{cause}")?;
if let Some(full_traceback) = full_traceback {
let traceback = traceback.trim_start_matches("stack traceback:");
let traceback = traceback.trim_start().trim_end();
// Try to find local traceback within the full traceback
if let Some(pos) = full_traceback.find(traceback) {
write!(fmt, "{}", &full_traceback[..pos])?;
writeln!(fmt, ">{}", &full_traceback[pos..].trim_end())?;
} else {
writeln!(fmt, "{}", full_traceback.trim_end())?;
}
} else {
writeln!(fmt, "{}", traceback.trim_end())?;
}
Ok(())
}
Error::PreviouslyResumedPanic => {
write!(fmt, "previously resumed panic returned again")
}
#[cfg(feature = "serialize")]
Error::SerializeError(ref err) => {
write!(fmt, "serialize error: {}", err)
Error::SerializeError(err) => {
write!(fmt, "serialize error: {err}")
},
#[cfg(feature = "serialize")]
Error::DeserializeError(ref err) => {
write!(fmt, "deserialize error: {}", err)
Error::DeserializeError(err) => {
write!(fmt, "deserialize error: {err}")
},
Error::ExternalError(ref err) => write!(fmt, "{}", err),
Error::ExternalError(err) => err.fmt(fmt),
Error::WithContext { context, cause } => {
writeln!(fmt, "{context}")?;
write!(fmt, "{cause}")
}
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
Error::ExternalError(ref err) => err.source(),
match self {
// An error type with a source error should either return that error via source or
// include that source's error message in its own Display output, but never both.
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
// returns nothing.
Error::CallbackError { .. } => None,
Error::ExternalError(err) => err.source(),
Error::WithContext { cause, .. } => Self::source(cause),
_ => None,
}
}
}
impl Error {
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
/// Creates a new `RuntimeError` with the given message.
#[inline]
pub fn runtime<S: fmt::Display>(message: S) -> Self {
Error::RuntimeError(message.to_string())
}
/// Wraps an external error object.
#[inline]
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
Error::ExternalError(err.into().into())
}
/// Attempts to downcast the external error object to a concrete type by reference.
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: StdError + 'static,
{
match self {
Error::ExternalError(err) => err.downcast_ref(),
Error::WithContext { cause, .. } => Self::downcast_ref(cause),
_ => None,
}
}
/// An iterator over the chain of nested errors wrapped by this Error.
pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
Chain {
root: self,
current: None,
}
}
/// Returns the parent of this error.
#[doc(hidden)]
pub fn parent(&self) -> Option<&Error> {
match self {
Error::CallbackError { cause, .. } => Some(cause.as_ref()),
Error::WithContext { cause, .. } => Some(cause.as_ref()),
_ => None,
}
}
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
Error::BadArgument {
to: Some(to.to_string()),
pos: 1,
name: Some("self".to_string()),
cause: Arc::new(cause),
}
}
pub(crate) fn from_lua_conversion(
from: &'static str,
to: impl ToString,
message: impl Into<Option<String>>,
) -> Self {
Error::FromLuaConversionError {
from,
to: to.to_string(),
message: message.into(),
}
}
}
/// Trait for converting [`std::error::Error`] into Lua [`Error`].
pub trait ExternalError {
fn to_lua_err(self) -> Error;
fn into_lua_err(self) -> Error;
}
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
fn to_lua_err(self) -> Error {
impl<E: Into<Box<DynStdError>>> ExternalError for E {
fn into_lua_err(self) -> Error {
Error::external(self)
}
}
/// Trait for converting [`std::result::Result`] into Lua [`Result`].
pub trait ExternalResult<T> {
fn to_lua_err(self) -> Result<T>;
fn into_lua_err(self) -> Result<T>;
}
impl<T, E> ExternalResult<T> for StdResult<T, E>
where
E: ExternalError,
{
fn to_lua_err(self) -> Result<T> {
self.map_err(|e| e.to_lua_err())
fn into_lua_err(self) -> Result<T> {
self.map_err(|e| e.into_lua_err())
}
}
impl std::convert::From<AddrParseError> for Error {
/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
pub trait ErrorContext: Sealed {
/// Wraps the error value with additional context.
fn context<C: fmt::Display>(self, context: C) -> Self;
/// Wrap the error value with additional context that is evaluated lazily
/// only once an error does occur.
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
}
impl ErrorContext for Error {
fn context<C: fmt::Display>(self, context: C) -> Self {
let context = context.to_string();
match self {
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
_ => Error::WithContext {
context,
cause: Arc::new(self),
},
}
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
let context = f(&self).to_string();
match self {
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
_ => Error::WithContext {
context,
cause: Arc::new(self),
},
}
}
}
impl<T> ErrorContext for Result<T> {
fn context<C: fmt::Display>(self, context: C) -> Self {
self.map_err(|err| err.context(context))
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
self.map_err(|err| err.with_context(f))
}
}
impl From<AddrParseError> for Error {
fn from(err: AddrParseError) -> Self {
Error::external(err)
}
}
impl std::convert::From<IoError> for Error {
impl From<IoError> for Error {
fn from(err: IoError) -> Self {
Error::external(err)
}
}
impl std::convert::From<Utf8Error> for Error {
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::external(err)
}
@@ -334,3 +507,64 @@ impl serde::de::Error for Error {
Self::DeserializeError(msg.to_string())
}
}
#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for Error {
fn from(err: anyhow::Error) -> Self {
match err.downcast::<Self>() {
Ok(err) => err,
Err(err) => Error::external(err),
}
}
}
struct Chain<'a> {
root: &'a Error,
current: Option<&'a (dyn StdError + 'static)>,
}
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn StdError + 'static);
fn next(&mut self) -> Option<Self::Item> {
loop {
let error: Option<&dyn StdError> = match self.current {
None => {
self.current = Some(self.root);
self.current
}
Some(current) => match current.downcast_ref::<Error>()? {
Error::BadArgument { cause, .. }
| Error::CallbackError { cause, .. }
| Error::WithContext { cause, .. } => {
self.current = Some(&**cause);
self.current
}
Error::ExternalError(err) => {
self.current = Some(&**err);
self.current
}
_ => None,
},
};
// Skip `ExternalError` as it only wraps the underlying error
// without meaningful context
if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
continue;
}
return self.current;
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "error-send"))]
static_assertions::assert_not_impl_any!(Error: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Error: Send, Sync);
}
-846
View File
@@ -1,846 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Based on github.com/keplerproject/lua-compat-5.3
#![allow(clippy::needless_return)]
use std::ffi::CStr;
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lauxlib::{
luaL_callmeta, luaL_error, luaL_getmetafield_old, luaL_loadbuffer, luaL_newmetatable_old,
};
#[cfg(any(feature = "lua51", feature = "luajit"))]
use super::lauxlib::{luaL_Reg, luaL_checktype, luaL_getmetatable};
#[cfg(feature = "lua52")]
use super::lauxlib::{luaL_checkstack, luaL_getsubtable};
use super::lua::{
self, lua_CFunction, lua_Debug, lua_Integer, lua_Number, lua_State, lua_Writer, lua_call,
lua_createtable, lua_dump_old, lua_error, lua_getfield_old, lua_getstack, lua_gettable_old,
lua_gettop, lua_insert, lua_isstring, lua_istable, lua_newuserdata, lua_pop, lua_pushboolean,
lua_pushcfunction, lua_pushfstring, lua_pushinteger, lua_pushliteral, lua_pushlstring_old,
lua_pushnil, lua_pushnumber, lua_pushthread, lua_pushvalue, lua_rawget_old, lua_rawgeti_old,
lua_rawset, lua_replace, lua_setfield, lua_setglobal, lua_setmetatable, lua_settable,
lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, lua_tostring,
lua_touserdata, lua_type, lua_typename,
};
#[cfg(any(feature = "lua51", feature = "luajit"))]
use super::lua::{
lua_checkstack, lua_concat, lua_equal, lua_getfenv, lua_getinfo, lua_getmetatable,
lua_isnumber, lua_lessthan, lua_newtable, lua_next, lua_objlen, lua_pushcclosure,
lua_pushlightuserdata, lua_pushstring_old, lua_rawequal, lua_remove, lua_resume_old,
lua_setfenv, lua_settop, LUA_OPADD, LUA_OPUNM,
};
#[cfg(feature = "lua52")]
use super::lua::{
lua_absindex, lua_getglobal_old, lua_getuservalue_old, lua_pushstring, lua_rawgetp_old,
lua_rawsetp, lua_tonumberx,
};
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
while a < b {
lua_pushvalue(L, a);
lua_pushvalue(L, b);
lua_replace(L, a);
lua_replace(L, b);
a += 1;
b -= 1;
}
}
const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack
const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack
unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int {
let mut ar: lua_Debug = mem::zeroed();
let (mut li, mut le) = (1, 1);
// find an upper bound
while lua_getstack(L, le, &mut ar) != 0 {
li = le;
le *= 2;
}
// do a binary search
while li < le {
let m = (li + le) / 2;
if lua_getstack(L, m, &mut ar) != 0 {
li = m + 1
} else {
le = m;
}
}
le - 1
}
unsafe fn compat53_checkmode(
L: *mut lua_State,
mode: *const c_char,
modename: *const c_char,
err: c_int,
) -> c_int {
unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char {
let mut st = s;
while *st != 0 && *st != c {
st = st.offset(1);
}
if *st == c {
st
} else {
ptr::null()
}
}
if !mode.is_null() && strchr(mode, *modename).is_null() {
lua_pushfstring(
L,
cstr!("attempt to load a %s chunk (mode is '%s')"),
modename,
mode,
);
return err;
}
lua::LUA_OK
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int {
if level == 0 || lua_istable(L, -1) == 0 {
return 0; // not found
}
lua_pushnil(L); // start 'next' loop
while lua_next(L, -2) != 0 {
// for each pair in table
if lua_type(L, -2) == lua::LUA_TSTRING {
// ignore non-string keys
if lua_rawequal(L, objidx, -1) != 0 {
// found object?
lua_pop(L, 1); // remove value (but keep name)
return 1;
} else if compat53_findfield(L, objidx, level - 1) != 0 {
// try recursively
lua_remove(L, -2); // remove table (but keep name)
lua_pushliteral(L, ".");
lua_insert(L, -2); // place '.' between the two names
lua_concat(L, 3);
return 1;
}
}
lua_pop(L, 1); // remove value
}
return 0; // not found
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> c_int {
let top = lua_gettop(L);
lua_getinfo(L, cstr!("f"), ar); // push function
lua_pushvalue(L, lua::LUA_GLOBALSINDEX);
if compat53_findfield(L, top + 1, 2) != 0 {
lua_copy(L, -1, top + 1); // move name to proper place
lua_pop(L, 2); // remove pushed values
return 1;
} else {
lua_settop(L, top); // remove function and global table
return 0;
}
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) {
if *(*ar).namewhat != b'\0' as c_char {
// is there a name?
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
} else if *(*ar).what == b'm' as c_char {
// main?
lua_pushliteral(L, "main chunk");
} else if *(*ar).what == b'C' as c_char {
if compat53_pushglobalfuncname(L, ar) != 0 {
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
lua_remove(L, -2); // remove name
} else {
lua_pushliteral(L, "?");
}
} else {
lua_pushfstring(
L,
cstr!("function <%s:%d>"),
(*ar).short_src.as_ptr(),
(*ar).linedefined,
);
}
}
unsafe fn compat53_call_lua(L: *mut lua_State, code: &str, nargs: c_int, nret: c_int) {
lua_rawgetp(L, lua::LUA_REGISTRYINDEX, code.as_ptr() as *const c_void);
if lua_type(L, -1) != lua::LUA_TFUNCTION {
lua_pop(L, 1);
if luaL_loadbuffer(
L,
code.as_ptr() as *const c_char,
code.as_bytes().len(),
cstr!("=none"),
) != 0
{
lua_error(L);
}
lua_pushvalue(L, -1);
lua_rawsetp(L, lua::LUA_REGISTRYINDEX, code.as_ptr() as *const c_void);
}
lua_insert(L, -nargs - 1);
lua_call(L, nargs, nret);
}
//
// lua ported functions
//
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub fn lua_upvalueindex(i: c_int) -> c_int {
lua::LUA_GLOBALSINDEX - i
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
if idx < 0 && idx > lua::LUA_REGISTRYINDEX {
idx += lua_gettop(L) + 1;
}
idx
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
static COMPAT53_ARITH_CODE: &str = r#"
local op,a,b=...
if op == 0 then return a+b
elseif op == 1 then return a-b
elseif op == 2 then return a*b
elseif op == 3 then return a/b
elseif op == 4 then return a%b
elseif op == 5 then return a^b
elseif op == 6 then return -a
end
"#;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn lua_arith(L: *mut lua_State, op: c_int) {
#[allow(clippy::manual_range_contains)]
if op < LUA_OPADD || op > LUA_OPUNM {
luaL_error(L, cstr!("invalid 'op' argument for lua_arith"));
}
luaL_checkstack(L, 5, cstr!("not enough stack slots"));
if op == LUA_OPUNM {
lua_pushvalue(L, -1);
}
lua_pushnumber(L, op as lua_Number);
lua_insert(L, -3);
compat53_call_lua(L, COMPAT53_ARITH_CODE, 3, 1);
}
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
}
if n > 0 && n < n_elems {
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
n = n_elems - n;
compat53_reverse(L, idx, idx + n - 1);
compat53_reverse(L, idx + n, idx + n_elems - 1);
compat53_reverse(L, idx, idx + n_elems - 1);
}
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
let abs_to = lua_absindex(L, toidx);
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
#[inline(always)]
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == lua::LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
return 1;
}
}
return 0;
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
let n = lua_tonumber(L, i);
if !isnum.is_null() {
*isnum = if n != 0.0 || lua_isnumber(L, i) != 0 {
1
} else {
0
};
}
return n;
}
// Implemented for Lua 5.2 as well
// See https://github.com/keplerproject/lua-compat-5.3/issues/40
#[inline(always)]
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);
let n_int = n as lua_Integer;
if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if !isnum.is_null() {
*isnum = 1;
}
return n_int;
}
if !isnum.is_null() {
*isnum = 0;
}
return 0;
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
lua_objlen(L, idx)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, op: c_int) -> c_int {
match op {
lua::LUA_OPEQ => lua_equal(L, idx1, idx2),
lua::LUA_OPLT => lua_lessthan(L, idx1, idx2),
lua::LUA_OPLE => {
luaL_checkstack(L, 5, cstr!("not enough stack slots"));
idx1 = lua_absindex(L, idx1);
idx2 = lua_absindex(L, idx2);
lua_pushvalue(L, idx1);
lua_pushvalue(L, idx2);
compat53_call_lua(L, "local a,b=...\nreturn a<=b\n", 2, 1);
let result = lua_toboolean(L, -1);
lua_pop(L, 1);
result
}
_ => luaL_error(L, cstr!("invalid 'op' argument for lua_compare")),
}
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
if l == 0 {
lua_pushlstring_old(L, cstr!(""), 0);
} else {
lua_pushlstring_old(L, s, l);
}
lua_tostring(L, -1)
}
#[cfg(feature = "lua52")]
#[inline(always)]
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
if l == 0 {
lua_pushlstring_old(L, cstr!(""), 0)
} else {
lua_pushlstring_old(L, s, l)
}
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
lua_pushstring_old(L, s);
lua_tostring(L, -1)
}
#[cfg(feature = "lua52")]
#[inline(always)]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getglobal_old(L, var);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
lua_gettable_old(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
lua_getfield_old(L, idx, k);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_gettable(L, idx);
lua_type(L, -1)
}
// A new version which returns c_int
#[inline(always)]
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
lua_rawget_old(L, idx);
lua_type(L, -1)
}
// A new version which returns c_int
#[inline(always)]
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
lua_rawgeti_old(L, idx, n);
lua_type(L, -1)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
let abs_i = lua_absindex(L, idx);
lua_pushlightuserdata(L, p as *mut c_void);
lua_rawget(L, abs_i);
lua_type(L, -1)
}
#[cfg(feature = "lua52")]
#[inline(always)]
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
lua_rawgetp_old(L, idx, p);
lua_type(L, -1)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getfenv(L, idx);
lua_type(L, -1)
}
#[cfg(feature = "lua52")]
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getuservalue_old(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_insert(L, -2);
lua_settable(L, idx);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
luaL_checktype(L, -1, lua::LUA_TTABLE);
lua_setfenv(L, idx);
}
#[inline(always)]
pub unsafe fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
data: *mut c_void,
_strip: c_int,
) -> c_int {
lua_dump_old(L, writer, data)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int) -> c_int {
lua_resume_old(L, narg)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
match lua_type(L, idx) {
lua::LUA_TSTRING => {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
lua::LUA_TTABLE => {
if luaL_callmeta(L, idx, cstr!("__len")) == 0 {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
}
lua::LUA_TUSERDATA if luaL_callmeta(L, idx, cstr!("__len")) != 0 => {}
_ => {
luaL_error(
L,
cstr!("attempt to get length of a %s value"),
lua_typename(L, lua_type(L, idx)),
);
}
}
}
#[inline(always)]
pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
use std::str::FromStr;
let cs = CStr::from_ptr(s);
if let Ok(rs) = cs.to_str() {
if let Ok(n) = f64::from_str(rs.trim()) {
lua_pushnumber(L, n as lua_Number);
return cs.to_bytes_with_nul().len();
}
}
0
}
#[allow(clippy::branches_sharing_code)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
use super::glue::LUA_EXTRASPACE;
luaL_checkstack(L, 4, cstr!("not enough stack slots available"));
lua_pushliteral(L, "__compat53_extraspace");
lua_pushvalue(L, -1);
lua_rawget(L, lua::LUA_REGISTRYINDEX);
if lua_istable(L, -1) == 0 {
lua_pop(L, 1);
lua_createtable(L, 0, 2);
lua_createtable(L, 0, 1);
lua_pushliteral(L, "k");
lua_setfield(L, -2, cstr!("__mode"));
lua_setmetatable(L, -2);
lua_pushvalue(L, -2);
lua_pushvalue(L, -2);
lua_rawset(L, lua::LUA_REGISTRYINDEX);
}
lua_replace(L, -2);
let is_main = lua_pushthread(L);
lua_rawget(L, -2);
let mut _ptr = lua_touserdata(L, -1);
if _ptr.is_null() {
lua_pop(L, 1);
_ptr = lua_newuserdata(L, LUA_EXTRASPACE as usize);
if is_main != 0 {
// mem::size_of::<c_void>() == 1
ptr::write_bytes(_ptr, 0, LUA_EXTRASPACE as usize);
lua_pushthread(L);
lua_pushvalue(L, -2);
lua_rawset(L, -4);
lua_pushboolean(L, 1);
lua_pushvalue(L, -2);
lua_rawset(L, -4);
} else {
lua_pushboolean(L, 1);
lua_rawget(L, -3);
let mptr = lua_touserdata(L, -1);
if !mptr.is_null() {
ptr::copy_nonoverlapping(mptr, _ptr, LUA_EXTRASPACE as usize)
} else {
ptr::write_bytes(_ptr, 0, LUA_EXTRASPACE as usize);
}
lua_pop(L, 1);
lua_pushthread(L);
lua_pushvalue(L, -2);
lua_rawset(L, -4);
}
}
lua_pop(L, 2);
return _ptr;
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
lua_pushvalue(L, lua::LUA_GLOBALSINDEX);
}
//
// lauxlib ported functions
//
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
if lua_checkstack(L, sz + lua::LUA_MINSTACK) == 0 {
if !msg.is_null() {
luaL_error(L, cstr!("stack overflow (%s)"), msg);
} else {
lua_pushliteral(L, "stack overflow");
lua_error(L);
}
}
}
pub unsafe fn luaL_checkversion(_L: *mut lua_State) {
// Void
}
#[inline(always)]
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
if luaL_getmetafield_old(L, obj, e) != 0 {
lua_type(L, -1)
} else {
lua::LUA_TNIL
}
}
#[inline(always)]
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
if luaL_newmetatable_old(L, tname) != 0 {
lua_pushstring(L, tname);
lua_setfield(L, -2, cstr!("__name"));
1
} else {
0
}
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int {
let status = if sz > 0 && *buff as u8 == lua::LUA_SIGNATURE[0] {
compat53_checkmode(L, mode, cstr!("binary"), lua::LUA_ERRSYNTAX)
} else {
compat53_checkmode(L, mode, cstr!("text"), lua::LUA_ERRSYNTAX)
};
if status != lua::LUA_OK {
return status;
}
luaL_loadbuffer(L, buff, sz, name)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua::lua_pop(L, 1);
if isnum == 0 {
luaL_error(L, cstr!("object length is not an integer"));
}
res
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn luaL_traceback(
L: *mut lua_State,
L1: *mut lua_State,
msg: *const c_char,
mut level: c_int,
) {
let mut ar: lua_Debug = mem::zeroed();
let top = lua_gettop(L);
let numlevels = compat53_countlevels(L1);
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
COMPAT53_LEVELS1
} else {
0
};
if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg);
}
lua_pushliteral(L, "stack traceback:");
while lua_getstack(L1, level, &mut ar) != 0 {
level += 1;
if level == mark {
// too many levels?
lua_pushliteral(L, "\n\t..."); // add a '...'
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
} else {
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
if ar.currentline > 0 {
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
}
lua_pushliteral(L, " in ");
compat53_pushfuncname(L, &mut ar);
lua_concat(L, lua_gettop(L) - top);
}
}
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
let t = lua_type(L, idx);
match t {
lua::LUA_TNIL => {
lua_pushliteral(L, "nil");
}
lua::LUA_TSTRING | lua::LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
lua::LUA_TBOOLEAN => {
if lua_toboolean(L, idx) == 0 {
lua_pushliteral(L, "false");
} else {
lua_pushliteral(L, "true");
}
}
_ => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == lua::LUA_TSTRING {
lua_tostring(L, -1)
} else {
lua_typename(L, t)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != lua::LUA_TNIL {
lua_replace(L, -2);
}
}
};
} else if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
lua_tolstring(L, -1, len)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void {
let mut p = lua_touserdata(L, i);
luaL_checkstack(L, 2, cstr!("not enough stack slots"));
if p.is_null() || lua_getmetatable(L, i) == 0 {
return ptr::null_mut();
} else {
luaL_getmetatable(L, tname);
let res = lua_rawequal(L, -1, -2);
lua_pop(L, 2);
if res == 0 {
p = ptr::null_mut();
}
}
return p;
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_int) {
luaL_checkstack(L, nup + 1, cstr!("too many upvalues"));
while !(*l).name.is_null() {
// fill the table with given functions
l = l.offset(1);
lua_pushstring(L, (*l).name);
for _ in 0..nup {
// copy upvalues to the top
lua_pushvalue(L, -(nup + 1));
}
lua_pushcclosure(L, (*l).func, nup); // closure with those upvalues
lua_settable(L, -(nup + 3)); // table must be below the upvalues, the name and the closure
}
lua_pop(L, nup); // remove upvalues
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
lua_pushstring(L, fname);
lua_gettable(L, abs_i);
if lua_istable(L, -1) != 0 {
return 1;
}
lua_pop(L, 1);
lua_newtable(L);
lua_pushstring(L, fname);
lua_pushvalue(L, -2);
lua_settable(L, abs_i);
return 0;
}
pub unsafe fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, lua::LUA_REGISTRYINDEX, cstr!("_LOADED"));
if lua_getfield(L, -1, modname) == lua::LUA_TNIL {
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);
#[cfg(any(feature = "lua52", feature = "lua51"))]
{
lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
}
#[cfg(feature = "luajit")]
{
lua_call(L, 1, 0);
lua_getfield(L, -1, modname);
}
}
if cfg!(any(feature = "lua52", feature = "lua51")) && glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
}
if cfg!(feature = "luajit") && glb == 0 {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
-289
View File
@@ -1,289 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#ifndef LUA_EXTRASPACE
#define LUA_EXTRASPACE (sizeof(void*))
#endif
// Macros taken from https://gcc.gnu.org/onlinedocs/cpp/Stringification.html
#define xstr(s) str(s)
#define str(s) #s
typedef struct rs_item {
int type;
const char *name;
union {
int int_val;
const char *str_val;
LUA_INTEGER lua_int_val;
};
} rs_item;
#define TY_INT 0
#define RS_INT(name, val) \
{ TY_INT, name, .int_val = val }
#if LUA_VERSION_NUM >= 503
#define TY_LUAINT 1
#define RS_LUAINT(name, val) \
{ TY_LUAINT, name, .lua_int_val = val }
#endif
#define TY_STR 2
#define RS_STR(name, val) \
{ TY_STR, name, .str_val = val }
#define TY_TYPE 3
#define RS_TYPE(name, val) \
{ TY_TYPE, name, .str_val = val }
#define TY_COMMENT 4
#define RS_COMMENT(val) \
{ TY_COMMENT, NULL, .str_val = val }
#define TY_RAW 5
#define RS_RAW(val) \
{ TY_RAW, NULL, .str_val = val }
const char *rs_int_type(int width) {
switch (width) {
default:
case 2:
return "i16";
case 4:
return "i32";
case 8:
return "i64";
case 16:
return "i128";
}
}
const char *rs_uint_type(int width) {
switch (width) {
default:
case 2:
return "u16";
case 4:
return "u32";
case 8:
return "u64";
case 16:
return "u128";
}
}
int try_write(char **str, char c, size_t n, size_t *written, size_t szstr) {
if (szstr - *written < n) {
return 0;
}
for (; n; n--, *written++)
*(*str)++ = c;
return 1;
}
// converts \ in a string to \\ so that it can be used as a rust string literal
// ensures that `out` will always have a null terminating character
size_t escape(const char *in, char *out, size_t szout) {
size_t written = 0;
char cur;
while ((cur = *in++)) {
switch (cur) {
case '\\':
if (!try_write(&out, cur, 2, &written, szout))
goto finalize;
break;
default:
if (!try_write(&out, cur, 1, &written, szout))
goto finalize;
break;
}
}
finalize:
if (written + 1 <= szout) {
*out++ = '\0';
written++;
}
return written;
}
int write_int_item(FILE *f, const char *name, int value) {
return fprintf(f, "pub const %s: c_int = %d;\n", name, value);
}
#if LUA_VERSION_NUM >= 503
int write_lua_int_item(FILE *f, const char *name, LUA_INTEGER value) {
return fprintf(f, "pub const %s: LUA_INTEGER = " LUA_INTEGER_FMT ";\n", name,
value);
}
#endif
int write_str_item(FILE *f, const char *name, const char *value) {
size_t len = strlen(value);
size_t bufsz = len * 2 + 1;
char *buf = malloc(bufsz);
int ret;
escape(value, buf, bufsz);
ret = fprintf(f, "pub const %s: &str = \"%s\";\n", name, buf);
free(buf);
return ret;
}
int write_type(FILE *f, const char *name, const char *value) {
return fprintf(f, "pub type %s = %s;\n", name, value);
}
int write_comment(FILE *f, const char *value) {
return fprintf(f, "/* %s */\n", value);
}
int write_raw(FILE *f, const char *value) { return fputs(value, f) >= 0; }
int write_item(FILE *f, const rs_item *c) {
switch (c->type) {
case TY_INT:
return write_int_item(f, c->name, c->int_val);
#if LUA_VERSION_NUM >= 503
case TY_LUAINT:
return write_lua_int_item(f, c->name, c->lua_int_val);
#endif
case TY_STR:
return write_str_item(f, c->name, c->str_val);
case TY_TYPE:
return write_type(f, c->name, c->str_val);
case TY_COMMENT:
return write_comment(f, c->str_val);
case TY_RAW:
return write_raw(f, c->str_val);
default:
return 0;
}
}
int write_items_(FILE *f, const rs_item items[], size_t num) {
size_t i;
for (i = 0; i < num; i++) {
if (!write_item(f, &items[i]))
return 0;
}
return 1;
}
#define write_items(f, cs) write_items_(f, cs, sizeof(cs) / sizeof(cs[0]))
int main(int argc, const char **argv) {
if (argc <= 1) {
printf("usage: %s <filename>\n", argv[0]);
return EXIT_FAILURE;
}
const char *filename = argv[1];
FILE *f = fopen(filename, "w");
if (!f) {
printf("could not open file: errno = %d\n", errno);
return EXIT_FAILURE;
}
const rs_item glue_entries[] = {
RS_COMMENT("this file was generated by glue.c; do not modify it by hand"),
RS_RAW("use std::os::raw::*;\n"),
// == luaconf.h ==========================================================
RS_COMMENT("luaconf.h"),
RS_INT("LUA_EXTRASPACE", LUA_EXTRASPACE),
RS_INT("LUA_IDSIZE", LUA_IDSIZE),
RS_TYPE("LUA_NUMBER",
sizeof(LUA_NUMBER) > sizeof(float) ? "c_double" : "c_float"),
RS_TYPE("LUA_INTEGER", rs_int_type(sizeof(LUA_INTEGER))),
#if LUA_VERSION_NUM >= 502
RS_TYPE("LUA_UNSIGNED", rs_uint_type(sizeof(LUA_UNSIGNED))),
#else
RS_TYPE("LUA_UNSIGNED", rs_uint_type(sizeof(size_t))),
#endif
// == lua.h ==============================================================
RS_COMMENT("lua.h"),
RS_INT("LUA_VERSION_NUM", LUA_VERSION_NUM),
RS_INT("LUA_REGISTRYINDEX", LUA_REGISTRYINDEX),
#if LUA_VERSION_NUM == 501
RS_INT("LUA_ENVIRONINDEX", LUA_ENVIRONINDEX),
RS_INT("LUA_GLOBALSINDEX", LUA_GLOBALSINDEX),
#endif
// == lauxlib.h ==========================================================
RS_COMMENT("lauxlib.h"),
#if LUA_VERSION_NUM >= 503
RS_INT("LUAL_NUMSIZES", LUAL_NUMSIZES),
#endif
// == lualib.h ===========================================================
RS_COMMENT("lualib.h"),
RS_STR("LUA_COLIBNAME", LUA_COLIBNAME),
RS_STR("LUA_TABLIBNAME", LUA_TABLIBNAME),
RS_STR("LUA_IOLIBNAME", LUA_IOLIBNAME),
RS_STR("LUA_OSLIBNAME", LUA_OSLIBNAME),
RS_STR("LUA_STRLIBNAME", LUA_STRLIBNAME),
#ifdef LUA_UTF8LIBNAME
RS_STR("LUA_UTF8LIBNAME", LUA_UTF8LIBNAME),
#endif
#ifdef LUA_BITLIBNAME
RS_STR("LUA_BITLIBNAME", LUA_BITLIBNAME),
#endif
RS_STR("LUA_MATHLIBNAME", LUA_MATHLIBNAME),
RS_STR("LUA_DBLIBNAME", LUA_DBLIBNAME),
RS_STR("LUA_LOADLIBNAME", LUA_LOADLIBNAME),
#ifdef LUA_JITLIBNAME
RS_STR("LUA_JITLIBNAME", LUA_JITLIBNAME),
#endif
#ifdef LUA_FFILIBNAME
RS_STR("LUA_FFILIBNAME", LUA_FFILIBNAME),
#endif
};
if (!write_items(f, glue_entries)) {
printf("%s: error generating %s; aborting\n", argv[0], filename);
return EXIT_FAILURE;
}
fclose(f);
return EXIT_SUCCESS;
}
-306
View File
@@ -1,306 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//! Contains definitions from `lauxlib.h`.
use std::os::raw::{c_char, c_int, c_long, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUAL_NUMSIZES;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub use super::compat53::{luaL_getmetafield, luaL_newmetatable, luaL_requiref, luaL_tolstring};
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::compat53::{
luaL_checkstack, luaL_checkversion, luaL_getsubtable, luaL_len, luaL_loadbufferx,
luaL_setfuncs, luaL_setmetatable, luaL_testudata, luaL_traceback,
};
// extra error code for 'luaL_load'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn luaL_checkversion(L: *mut lua_State) {
#[cfg(any(feature = "lua54", feature = "lua53"))]
luaL_checkversion_(
L,
lua::LUA_VERSION_NUM as lua_Number,
LUAL_NUMSIZES as usize,
);
#[cfg(feature = "lua52")]
luaL_checkversion_(L, lua::LUA_VERSION_NUM as lua_Number);
}
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
#[cfg(feature = "lua52")]
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_old(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, l: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, arg: c_int);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "luaL_newmetatable"]
pub fn luaL_newmetatable_old(L: *mut lua_State, tname: *const c_char) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
// TODO: test this
pub fn luaL_checkoption(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
}
// pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
extern "C" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r: c_int);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
-> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
luaL_loadfilex(L, f, ptr::null())
}
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn luaL_loadbuffer(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
// TODO (lua54): luaL_addgsub
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
p: *const c_char,
r: *const c_char,
) -> *const c_char;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
// Skip Lua 5.2 implementation in favor of the compat53 one
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
);
}
#[inline(always)]
#[allow(unused_variables)]
pub unsafe fn luaL_newlibtable(L: *mut lua_State, l: *const luaL_Reg) {
// TODO: figure out how to pass an appropriate hint for the second param
// this involves correcting the second parameter's type; in C this is
// sizeof(l)/sizeof(l[0])
lua::lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn luaL_newlib(L: *mut lua_State, l: *const luaL_Reg) {
luaL_checkversion(L);
luaL_newlibtable(L, l);
luaL_setfuncs(L, l, 0)
}
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, arg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
// From 5.3 user manual:
// Macros to project non-default integer types (luaL_checkint, luaL_optint,
// luaL_checklong, luaL_optlong) were deprecated. Use their equivalent over
// lua_Integer with a type cast (or, when possible, use lua_Integer in your
// code).
#[inline(always)]
//#[deprecated]
pub unsafe fn luaL_checkint(L: *mut lua_State, n: c_int) -> c_int {
luaL_checkinteger(L, n) as c_int
}
#[inline(always)]
//#[deprecated]
pub unsafe fn luaL_optint(L: *mut lua_State, n: c_int, d: c_int) -> c_int {
luaL_optinteger(L, n, d as lua_Integer) as c_int
}
#[inline(always)]
//#[deprecated]
pub unsafe fn luaL_checklong(L: *mut lua_State, n: c_int) -> c_long {
luaL_checkinteger(L, n) as c_long
}
#[inline(always)]
//#[deprecated]
pub unsafe fn luaL_optlong(L: *mut lua_State, n: c_int, d: c_long) -> c_long {
luaL_optinteger(L, n, d as lua_Integer) as c_long
}
#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
lua::lua_typename(L, lua::lua_type(L, i))
}
#[inline(always)]
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
let status = luaL_loadfile(L, filename);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
let status = luaL_loadstring(L, s);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
}
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
s: *const c_char,
sz: usize,
n: *const c_char,
) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
// TODO: Add buffer API
// omitted: old module system compatibility (removed in 5.4)
-829
View File
@@ -1,829 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//! Contains definitions from `lua.h`.
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use std::os::raw::c_uchar;
use std::os::raw::{c_char, c_int, c_void};
#[cfg(feature = "lua54")]
use std::os::raw::{c_uint, c_ushort};
use std::ptr;
use super::luaconf;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
pub use super::glue::{LUA_REGISTRYINDEX, LUA_VERSION_NUM};
#[cfg(not(feature = "luajit"))]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
#[cfg(feature = "luajit")]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLJ";
// option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub use super::compat53::{
lua_dump, lua_getextraspace, lua_getfield, lua_geti, lua_gettable, lua_getuservalue,
lua_isinteger, lua_pushlstring, lua_rawget, lua_rawgeti, lua_rawgetp, lua_rotate, lua_seti,
lua_stringtonumber, lua_tointegerx,
};
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::compat53::{
lua_absindex, lua_arith, lua_compare, lua_copy, lua_len, lua_pushglobaltable, lua_pushstring,
lua_rawlen, lua_rawsetp, lua_resume as lua_resume_53, lua_setuservalue, lua_tonumberx,
lua_upvalueindex,
};
#[cfg(feature = "lua52")]
pub use super::compat53::lua_getglobal;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i
}
// thread status
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_ERRGCMM: c_int = 5;
#[cfg(any(feature = "lua54", feature = "lua51", feature = "luajit"))]
pub const LUA_ERRERR: c_int = 5;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_ERRERR: c_int = 6;
pub type lua_State = c_void;
// basic types
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
#[cfg(feature = "lua54")]
pub const LUA_NUMTYPES: c_int = 9;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_NUMTAGS: c_int = 9;
// minimum stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
// predefined values in the registry
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
// 5.4 uses `luaL_Buffer`
pub const LUA_TRACEBACK_STACK: c_int = 11;
/// A Lua number, usually equivalent to `f64`.
pub type lua_Number = luaconf::LUA_NUMBER;
/// A Lua integer, usually equivalent to `i64`.
pub type lua_Integer = luaconf::LUA_INTEGER;
// unsigned integer type
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
// type for continuation-function contexts
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub type lua_KContext = luaconf::LUA_KCONTEXT;
/// Type for native functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
// Type for continuation functions
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub type lua_KFunction =
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
pub type lua_Reader =
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
pub type lua_Writer =
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
// Type for memory-allocation functions.
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
// Type for warning functions
#[cfg(feature = "lua54")]
pub type lua_WarnFunction =
unsafe extern "C" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
extern "C" {
// state manipulation
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
#[cfg(feature = "lua54")]
#[link_name = "lua_resetthread"]
pub fn lua_resetthread_54(L: *mut lua_State) -> c_int;
#[cfg(all(feature = "luajit", feature = "vendored"))]
#[link_name = "lua_resetthread"]
pub fn lua_resetthread_jit(L: *mut lua_State, th: *mut lua_State);
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
#[cfg(feature = "lua54")]
pub fn lua_version(L: *mut lua_State) -> lua_Number;
#[cfg(feature = "lua53")]
pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
// basic stack manipulation
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub fn lua_remove(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub fn lua_insert(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub fn lua_replace(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
// access functions (stack -> C)
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_tointeger(L: *mut lua_State, idx: c_int) -> lua_Integer;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> lua_CFunction;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
}
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
pub unsafe fn lua_resetthread(_L: *mut lua_State, th: *mut lua_State) -> c_int {
#[cfg(all(feature = "luajit", feature = "vendored"))]
{
lua_resetthread_jit(_L, th);
LUA_OK
}
#[cfg(feature = "lua54")]
lua_resetthread_54(th)
}
// Comparison and arithmetic functions
pub const LUA_OPADD: c_int = 0;
pub const LUA_OPSUB: c_int = 1;
pub const LUA_OPMUL: c_int = 2;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub const LUA_OPDIV: c_int = 3;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub const LUA_OPMOD: c_int = 4;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub const LUA_OPPOW: c_int = 5;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
pub const LUA_OPUNM: c_int = 6;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPMOD: c_int = 3;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPPOW: c_int = 4;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPDIV: c_int = 5;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPIDIV: c_int = 6;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBAND: c_int = 7;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBOR: c_int = 8;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBXOR: c_int = 9;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPSHL: c_int = 10;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPSHR: c_int = 11;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPUNM: c_int = 12;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub const LUA_OPBNOT: c_int = 13;
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_arith(L: *mut lua_State, op: c_int);
}
pub const LUA_OPEQ: c_int = 0;
pub const LUA_OPLT: c_int = 1;
pub const LUA_OPLE: c_int = 2;
extern "C" {
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_equal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
}
// push functions (C -> stack)
extern "C" {
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_old(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[link_name = "lua_pushstring"]
pub fn lua_pushstring_old(L: *mut lua_State, s: *const c_char) -> *const c_char;
// TODO: omitted:
// lua_pushvfstring
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
}
// get functions (Lua -> stack)
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int;
#[cfg(feature = "lua52")]
#[link_name = "lua_getglobal"]
pub fn lua_getglobal_old(L: *mut lua_State, var: *const c_char);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_gettable"]
pub fn lua_gettable_old(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_getfield"]
pub fn lua_getfield_old(L: *mut lua_State, idx: c_int, k: *const c_char);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_rawget"]
pub fn lua_rawget_old(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_rawgeti"]
pub fn lua_rawgeti_old(L: *mut lua_State, idx: c_int, n: lua_Integer);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int;
#[cfg(feature = "lua52")]
#[link_name = "lua_rawgetp"]
pub fn lua_rawgetp_old(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
#[cfg(feature = "lua54")]
pub fn lua_newuserdatauv(L: *mut lua_State, sz: usize, nuvalue: c_int) -> *mut c_void;
#[cfg(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
))]
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_getiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
#[cfg(feature = "lua53")]
pub fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int;
#[cfg(feature = "lua52")]
#[link_name = "lua_getuservalue"]
pub fn lua_getuservalue_old(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_getfenv(L: *mut lua_State, idx: c_int);
}
#[cfg(feature = "lua54")]
#[inline(always)]
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatauv(L, sz, 1)
}
#[cfg(feature = "lua54")]
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getiuservalue(L, idx, 1)
}
// set functions (stack -> Lua)
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_setglobal(L: *mut lua_State, var: *const c_char);
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_setiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn lua_setuservalue(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
}
#[cfg(feature = "lua54")]
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
lua_setiuservalue(L, idx, 1);
}
// 'load' and 'call' functions (load and run Lua code)
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
);
#[cfg(feature = "lua52")]
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
ctx: c_int,
k: Option<lua_CFunction>,
);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
errfunc: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
#[cfg(feature = "lua52")]
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
errfunc: c_int,
ctx: c_int,
k: Option<lua_CFunction>,
) -> c_int;
#[cfg(feature = "lua52")]
pub fn lua_getctx(L: *mut lua_State, ctx: *mut c_int) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
// TODO
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
dt: *mut c_void,
chunkname: *const c_char,
mode: *const c_char,
) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
data: *mut c_void,
strip: c_int,
) -> c_int;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[link_name = "lua_dump"]
pub fn lua_dump_old(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int;
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) {
lua_callk(L, n, r, 0, None)
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int {
lua_pcallk(L, n, r, f, 0, None)
}
// coroutine functions
extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_yieldk(
L: *mut lua_State,
nresults: c_int,
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
#[cfg(feature = "lua52")]
pub fn lua_yieldk(
L: *mut lua_State,
nresults: c_int,
ctx: c_int,
k: Option<lua_CFunction>,
) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int;
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[link_name = "lua_resume"]
pub fn lua_resume_53(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[link_name = "lua_resume"]
pub fn lua_resume_old(L: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
lua_yieldk(L, n, 0, None)
}
#[cfg(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
))]
#[inline(always)]
pub unsafe fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int {
let ret = lua_resume_53(L, from, narg);
if ret == LUA_OK || ret == LUA_YIELD {
*nres = lua_gettop(L);
}
ret
}
// warning-related functions
#[cfg(feature = "lua54")]
extern "C" {
pub fn lua_setwarnf(L: *mut lua_State, f: lua_WarnFunction, ud: *mut c_void);
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
}
// garbage-collection function and options
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_GCISRUNNING: c_int = 9;
#[cfg(feature = "lua54")]
pub const LUA_GCGEN: c_int = 10;
#[cfg(feature = "lua54")]
pub const LUA_GCINC: c_int = 11;
extern "C" {
#[cfg(feature = "lua54")]
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
#[cfg(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
))]
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
// miscellaneous functions
extern "C" {
pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_len(L: *mut lua_State, idx: c_int);
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize;
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
#[cfg(feature = "lua54")]
pub fn lua_toclose(L: *mut lua_State, idx: c_int);
}
// some useful macros
// here, implemented as Rust functions
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
L.offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut())
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
lua_pushcfunction(L, f);
lua_setglobal(L, n)
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosure(L, f, 0)
}
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= 0) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_char {
use std::ffi::CString;
let c_str = CString::new(s).unwrap();
lua_pushlstring(L, c_str.as_ptr(), c_str.as_bytes().len())
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
lua_setfield(L, LUA_GLOBALSINDEX, var)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline(always)]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getfield(L, LUA_GLOBALSINDEX, var)
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, 1)
}
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, -1);
lua_pop(L, 1)
}
#[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)]
pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_copy(L, -1, idx);
lua_pop(L, 1)
}
// Debug API
// Event codes
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
// Event masks
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
extern "C" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua54")]
pub fn lua_setcstacklimit(L: *mut lua_State, limit: c_uint) -> c_int;
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
#[cfg(feature = "lua54")]
pub srclen: usize,
pub currentline: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub nups: c_uchar,
pub nparams: c_uchar,
pub isvararg: c_char,
pub istailcall: c_char,
#[cfg(feature = "lua54")]
pub ftransfer: c_ushort,
#[cfg(feature = "lua54")]
pub ntransfer: c_ushort,
pub short_src: [c_char; luaconf::LUA_IDSIZE as usize],
// lua.h mentions this is for private use
i_ci: *mut c_void,
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub currentline: c_int,
pub nups: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub short_src: [c_char; luaconf::LUA_IDSIZE as usize],
// lua.h mentions this is for private use
i_ci: c_int,
}
-36
View File
@@ -1,36 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//! Contains definitions from `luaconf.h`.
pub use super::glue::LUA_INTEGER;
pub use super::glue::LUA_NUMBER;
pub use super::glue::LUA_UNSIGNED;
pub use super::glue::LUA_IDSIZE;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUAL_NUMSIZES;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub type LUA_KCONTEXT = isize; // intptr_t
-67
View File
@@ -1,67 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//! Contains definitions from `lualib.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
pub use super::glue::{
LUA_COLIBNAME, LUA_DBLIBNAME, LUA_IOLIBNAME, LUA_LOADLIBNAME, LUA_MATHLIBNAME, LUA_OSLIBNAME,
LUA_STRLIBNAME, LUA_TABLIBNAME,
};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use super::glue::LUA_UTF8LIBNAME;
#[cfg(any(feature = "lua52", feature = "luajit"))]
pub use super::glue::LUA_BITLIBNAME;
#[cfg(feature = "luajit")]
pub use super::glue::{LUA_FFILIBNAME, LUA_JITLIBNAME};
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua52")]
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_bit(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_jit(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_ffi(L: *mut lua_State) -> c_int;
pub fn luaL_openlibs(L: *mut lua_State);
}
-315
View File
@@ -1,315 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019-2021 A. Orlenko
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//! Low level bindings to Lua.
#![allow(non_camel_case_types, non_snake_case, dead_code)]
use std::os::raw::c_int;
// This is more or less in the order it appears in the Lua manual, with the
// exception of constants, which appear scattered throughout the manual text.
// C API types
pub use self::lua::{
lua_Alloc, lua_CFunction, lua_Debug, lua_Hook, lua_Integer, lua_Number, lua_Reader, lua_State,
lua_Unsigned, lua_Writer,
};
#[cfg(feature = "lua54")]
pub use self::lua::lua_WarnFunction;
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{lua_KContext, lua_KFunction};
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::{lua_getfenv, lua_setfenv};
// C API functions
pub use self::lua::{
lua_absindex,
lua_arith,
lua_atpanic,
lua_call,
lua_checkstack,
lua_close,
lua_compare,
lua_concat,
lua_copy,
lua_createtable,
lua_dump,
lua_error,
lua_gc,
lua_getallocf,
lua_getextraspace,
lua_getfield,
lua_getglobal,
lua_gethook,
lua_gethookcount,
lua_gethookmask,
lua_geti,
lua_getinfo,
lua_getlocal,
lua_getmetatable,
lua_getstack,
lua_gettable,
lua_gettop,
lua_getupvalue,
lua_getuservalue,
lua_insert,
lua_isboolean,
lua_iscfunction,
lua_isfunction,
lua_isinteger,
lua_islightuserdata,
lua_isnil,
lua_isnone,
lua_isnoneornil,
lua_isnumber,
lua_isstring,
lua_istable,
lua_isthread,
lua_isuserdata,
lua_len,
lua_load,
lua_newstate,
lua_newtable,
lua_newthread,
lua_newuserdata,
lua_next,
lua_pcall,
lua_pop,
lua_pushboolean,
lua_pushcclosure,
lua_pushcfunction,
lua_pushfstring,
lua_pushglobaltable,
lua_pushinteger,
lua_pushlightuserdata,
lua_pushliteral,
lua_pushlstring,
lua_pushnil,
lua_pushnumber,
lua_pushstring,
lua_pushthread,
lua_pushvalue,
// omitted: lua_pushvfstring
lua_rawequal,
lua_rawget,
lua_rawgeti,
lua_rawgetp,
lua_rawlen,
lua_rawset,
lua_rawseti,
lua_rawsetp,
lua_register,
lua_remove,
lua_replace,
lua_resume,
lua_rotate,
lua_setallocf,
lua_setfield,
lua_setglobal,
lua_sethook,
lua_seti,
lua_setlocal,
lua_setmetatable,
lua_settable,
lua_settop,
lua_setupvalue,
lua_setuservalue,
lua_status,
lua_stringtonumber,
lua_toboolean,
lua_tocfunction,
lua_tointeger,
lua_tointegerx,
lua_tolstring,
lua_tonumber,
lua_tonumberx,
lua_topointer,
lua_tostring,
lua_tothread,
lua_touserdata,
lua_type,
lua_typename,
lua_upvalueindex,
lua_xmove,
lua_yield,
};
#[cfg(feature = "lua54")]
pub use self::lua::{
lua_getiuservalue, lua_newuserdatauv, lua_setcstacklimit, lua_setiuservalue, lua_setwarnf,
lua_toclose, lua_warning,
};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{lua_isyieldable, lua_version};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
pub use self::lua::lua_resetthread;
// auxiliary library types
pub use self::lauxlib::luaL_Reg;
// auxiliary library functions
pub use self::lauxlib::{
luaL_argcheck, luaL_argerror, luaL_callmeta, luaL_checkany, luaL_checkint, luaL_checkinteger,
luaL_checklong, luaL_checklstring, luaL_checknumber, luaL_checkoption, luaL_checkstack,
luaL_checkstring, luaL_checktype, luaL_checkudata, luaL_checkversion, luaL_dofile,
luaL_dostring, luaL_error, luaL_getmetafield, luaL_getmetatable, luaL_getsubtable, luaL_gsub,
luaL_len, luaL_loadbuffer, luaL_loadbufferx, luaL_loadfile, luaL_loadstring, luaL_newlib,
luaL_newlibtable, luaL_newmetatable, luaL_newstate, luaL_optint, luaL_optinteger, luaL_optlong,
luaL_optlstring, luaL_optnumber, luaL_optstring, luaL_ref, luaL_requiref, luaL_setfuncs,
luaL_setmetatable, luaL_testudata, luaL_tolstring, luaL_traceback, luaL_typename, luaL_unref,
luaL_where,
};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub use self::lauxlib::{luaL_execresult, luaL_fileresult, luaL_loadfilex};
// lualib.h functions
pub use self::lualib::{
luaL_openlibs, luaopen_base, luaopen_debug, luaopen_io, luaopen_math, luaopen_os,
luaopen_package, luaopen_string, luaopen_table,
};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lualib::{luaopen_coroutine, luaopen_utf8};
#[cfg(feature = "lua52")]
pub use self::lualib::{luaopen_bit32, luaopen_coroutine};
#[cfg(feature = "luajit")]
pub use self::lualib::{luaopen_bit, luaopen_ffi, luaopen_jit};
// constants from lua.h
pub use self::lua::{
LUA_ERRERR, LUA_ERRMEM, LUA_ERRRUN, LUA_ERRSYNTAX, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCCOUNTB,
LUA_GCRESTART, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCSTEP, LUA_GCSTOP, LUA_HOOKCALL,
LUA_HOOKCOUNT, LUA_HOOKLINE, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_MASKCALL, LUA_MASKCOUNT,
LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, LUA_OPADD, LUA_OPDIV, LUA_OPEQ,
LUA_OPLE, LUA_OPLT, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSUB, LUA_OPUNM, LUA_REGISTRYINDEX,
LUA_SIGNATURE, LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE,
LUA_TNUMBER, LUA_TRACEBACK_STACK, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
LUA_YIELD,
};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lua::{
LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPIDIV, LUA_OPSHL, LUA_OPSHR,
};
#[cfg(feature = "lua54")]
pub use self::lua::{LUA_GCGEN, LUA_GCINC};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub use self::lua::{LUA_GCISRUNNING, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD};
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub use self::lua::LUA_ERRGCMM;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
// constants from lauxlib.h
pub use self::lauxlib::{LUA_ERRFILE, LUA_NOREF, LUA_REFNIL};
// constants from lualib.h
pub use self::lualib::{
LUA_COLIBNAME, LUA_DBLIBNAME, LUA_IOLIBNAME, LUA_LOADLIBNAME, LUA_MATHLIBNAME, LUA_OSLIBNAME,
LUA_STRLIBNAME, LUA_TABLIBNAME,
};
#[cfg(any(feature = "lua54", feature = "lua53"))]
pub use self::lualib::LUA_UTF8LIBNAME;
#[cfg(any(feature = "lua52", feature = "luajit"))]
pub use self::lualib::LUA_BITLIBNAME;
#[cfg(feature = "luajit")]
pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
// Not actually defined in lua.h / luaconf.h
pub const LUA_MAX_UPVALUES: c_int = 255;
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
#[cfg(all(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc",
target_arch = "asmjs",
target_arch = "wasm32",
target_arch = "hexagon",
target_arch = "riscv32"
)))]
pub const SYS_MIN_ALIGN: usize = 8;
#[cfg(all(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64",
target_arch = "riscv64"
)))]
pub const SYS_MIN_ALIGN: usize = 16;
// Hack to avoid stripping a few unused Lua symbols that could be imported
// by C modules in unsafe mode
pub(crate) fn keep_lua_symbols() {
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
symbols.push(lua_atpanic as _);
symbols.push(lua_isuserdata as _);
symbols.push(lua_tocfunction as _);
symbols.push(luaL_loadstring as _);
symbols.push(luaL_openlibs as _);
if cfg!(any(any(
feature = "lua54",
feature = "lua53",
feature = "lua52"
))) {
symbols.push(lua_getglobal as _);
symbols.push(lua_setglobal as _);
}
}
#[allow(unused_imports, dead_code, non_camel_case_types)]
#[allow(clippy::unreadable_literal)]
mod glue {
include!(concat!(env!("OUT_DIR"), "/glue.rs"));
}
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
mod compat53;
mod lauxlib;
mod lua;
mod luaconf;
mod lualib;
+499 -91
View File
@@ -1,21 +1,66 @@
use std::cell::RefCell;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use std::{mem, ptr, slice};
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
use crate::state::Lua;
use crate::table::Table;
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
use crate::util::{
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
};
use crate::value::Value;
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
use {
crate::traits::LuaNativeAsyncFn,
crate::types::AsyncCallback,
std::future::{self, Future},
};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
#[derive(Clone, Debug, PartialEq)]
pub struct Function(pub(crate) ValueRef);
impl<'lua> Function<'lua> {
/// Contains information about a function.
///
/// Please refer to the [`Lua Debug Interface`] for more information.
///
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
#[derive(Clone, Debug)]
pub struct FunctionInfo {
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<String>,
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
/// the main part of a chunk.
pub what: &'static str,
/// Source of the chunk that created the function.
pub source: Option<String>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<String>,
/// The line number where the definition of the function starts.
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
}
/// Luau function coverage snapshot.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoverageInfo {
pub function: Option<String>,
pub line_defined: i32,
pub depth: i32,
pub hits: Vec<i32>,
}
impl Function {
/// Calls the function, passing `args` as function arguments.
///
/// The function's return values are converted to the generic type `R`.
@@ -32,7 +77,7 @@ impl<'lua> Function<'lua> {
///
/// let tostring: Function = globals.get("tostring")?;
///
/// assert_eq!(tostring.call::<_, String>(123)?, "123");
/// assert_eq!(tostring.call::<String>(123)?, "123");
///
/// # Ok(())
/// # }
@@ -51,44 +96,36 @@ impl<'lua> Function<'lua> {
/// end
/// "#).eval()?;
///
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
/// assert_eq!(sum.call::<u32>((3, 4))?, 3 + 4);
///
/// # Ok(())
/// # }
/// ```
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua;
pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 3)?;
ffi::lua_pushcfunction(lua.state, error_traceback);
let stack_start = ffi::lua_gettop(lua.state);
// Push error handler
lua.push_error_traceback();
let stack_start = ffi::lua_gettop(state);
// Push function and the arguments
lua.push_ref(&self.0);
for arg in args {
lua.push_value(arg)?;
}
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
let nargs = args.push_into_stack_multi(&lua)?;
// Call the function
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK {
return Err(pop_error(lua.state, ret));
return Err(pop_error(state, ret));
}
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = MultiValue::new();
assert_stack(lua.state, 2);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
ffi::lua_pop(lua.state, 1);
results
};
R::from_lua_multi(results, lua)
// Get the results
let nresults = ffi::lua_gettop(state) - stack_start;
R::from_stack_multi(nresults, &lua)
}
}
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
/// and drives the execution.
///
/// Internally it wraps the function to an [`AsyncThread`].
@@ -99,37 +136,38 @@ impl<'lua> Function<'lua> {
///
/// ```
/// use std::time::Duration;
/// use futures_timer::Delay;
/// # use mlua::{Lua, Result};
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
///
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
/// Delay::new(Duration::from_millis(n)).await;
/// tokio::time::sleep(Duration::from_millis(n)).await;
/// Ok(())
/// })?;
///
/// sleep.call_async(10).await?;
/// sleep.call_async::<()>(10).await?;
///
/// # Ok(())
/// # }
/// ```
///
/// [`AsyncThread`]: struct.AsyncThread.html
/// [`AsyncThread`]: crate::AsyncThread
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
R: FromLuaMulti,
{
let lua = self.0.lua;
match lua.create_thread(self.clone()) {
Ok(t) => Box::pin(t.into_async(args)),
Err(e) => Box::pin(future::err(e)),
}
let lua = self.0.lua.lock();
let thread_res = unsafe {
lua.create_recycled_thread(self).map(|th| {
let mut th = th.into_async(args);
th.set_recyclable(true);
th
})
};
async move { thread_res?.await }
}
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
@@ -151,58 +189,198 @@ impl<'lua> Function<'lua> {
/// "#).eval()?;
///
/// let bound_a = sum.bind(1)?;
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
/// assert_eq!(bound_a.call::<u32>(2)?, 1 + 2);
///
/// let bound_a_and_b = sum.bind(13)?.bind(57)?;
/// assert_eq!(bound_a_and_b.call::<_, u32>(())?, 13 + 57);
/// assert_eq!(bound_a_and_b.call::<u32>(())?, 13 + 57);
///
/// # Ok(())
/// # }
/// ```
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
ffi::luaL_checkstack(state, nbinds + 2, ptr::null());
ffi::lua_settop(state, nargs + nbinds + 1);
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
ffi::lua_replace(state, 1);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
ffi::luaL_checkstack(state, nbinds, ptr::null());
for i in 0..nbinds {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
ffi::lua_replace(state, i + 2);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
}
if nargs > 0 {
ffi::lua_rotate(state, 1, nbinds);
}
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
ffi::lua_gettop(state)
nargs + nbinds
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
let args = args.to_lua_multi(lua)?;
let args = args.into_lua_multi(lua.lua())?;
let nargs = args.len() as c_int;
if nargs + 2 > ffi::LUA_MAX_UPVALUES {
if nargs == 0 {
return Ok(self.clone());
}
if nargs + 1 > ffi::LUA_MAX_UPVALUES {
return Err(Error::BindError);
}
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 5)?;
let args_wrapper = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, nargs + 3)?;
lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
for arg in &args {
lua.push_value(arg)?;
}
protect_lua(lua.state, nargs + 2, 1, |state| {
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
protect_lua!(state, nargs + 1, 1, fn(state) {
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
})?;
Ok(Function(lua.pop_ref()))
Function(lua.pop_ref())
};
let lua = lua.lua();
lua.load(
r#"
local func, args_wrapper = ...
return function(...)
return func(args_wrapper(...))
end
"#,
)
.try_cache()
.set_name("__mlua_bind")
.call((self, args_wrapper))
}
/// Returns the environment of the Lua function.
///
/// By default Lua functions shares a global environment.
///
/// This function always returns `None` for Rust/C functions.
pub fn environment(&self) -> Option<Table> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
lua.push_ref(&self.0);
if ffi::lua_iscfunction(state, -1) != 0 {
return None;
}
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
ffi::lua_getfenv(state, -1);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
for i in 1..=255 {
// Traverse upvalues until we find the _ENV one
match ffi::lua_getupvalue(state, -1, i) {
s if s.is_null() => break,
s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => break,
_ => ffi::lua_pop(state, 1),
}
}
if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
return None;
}
Some(Table(lua.pop_ref()))
}
}
/// Sets the environment of the Lua function.
///
/// The environment is a table that is used as the global environment for the function.
/// Returns `true` if environment successfully changed, `false` otherwise.
///
/// This function does nothing for Rust/C functions.
pub fn set_environment(&self, env: Table) -> Result<bool> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
lua.push_ref(&self.0);
if ffi::lua_iscfunction(state, -1) != 0 {
return Ok(false);
}
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
{
lua.push_ref(&env.0);
ffi::lua_setfenv(state, -2);
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
for i in 1..=255 {
match ffi::lua_getupvalue(state, -1, i) {
s if s.is_null() => return Ok(false),
s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => {
ffi::lua_pop(state, 1);
// Create an anonymous function with the new environment
let f_with_env = lua
.lua()
.load("return _ENV")
.set_environment(env)
.try_cache()
.into_function()?;
lua.push_ref(&f_with_env.0);
ffi::lua_upvaluejoin(state, -2, i, -1, 1);
break;
}
_ => ffi::lua_pop(state, 1),
}
}
Ok(true)
}
}
/// Returns information about the function.
///
/// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function.
///
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
pub fn info(&self) -> FunctionInfo {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
let mut ar: ffi::lua_Debug = mem::zeroed();
lua.push_ref(&self.0);
#[cfg(not(feature = "luau"))]
let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
#[cfg(feature = "luau")]
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
#[cfg(not(feature = "luau"))]
name_what: match ptr_to_str(ar.namewhat) {
Some("") => None,
val => val,
},
#[cfg(feature = "luau")]
name_what: None,
what: ptr_to_str(ar.what).unwrap_or("main"),
source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
#[cfg(feature = "luau")]
short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
line_defined: linenumber_to_usize(ar.linedefined),
#[cfg(not(feature = "luau"))]
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: None,
}
}
}
@@ -210,8 +388,14 @@ impl<'lua> Function<'lua> {
///
/// If `strip` is true, the binary representation may not include all debug information
/// about the function, to save space.
///
/// For Luau a [`Compiler`] can be used to compile Lua chunks to bytecode.
///
/// [`Compiler`]: crate::chunk::Compiler
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn dump(&self, strip: bool) -> Vec<u8> {
unsafe extern "C" fn writer(
unsafe extern "C-unwind" fn writer(
_state: *mut ffi::lua_State,
buf: *const c_void,
buf_len: usize,
@@ -223,25 +407,249 @@ impl<'lua> Function<'lua> {
0
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
let mut data: Vec<u8> = Vec::new();
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
let _sg = StackGuard::new(state);
assert_stack(state, 1);
lua.push_ref(&self.0);
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
let strip = if strip { 1 } else { 0 };
ffi::lua_dump(lua.state, writer, data_ptr, strip);
ffi::lua_pop(lua.state, 1);
ffi::lua_dump(state, writer, data_ptr, strip as i32);
ffi::lua_pop(state, 1);
}
data
}
}
impl<'lua> PartialEq for Function<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
/// Retrieves recorded coverage information about this Lua function including inner calls.
///
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`]
/// snapshot per each executed inner function.
///
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
///
/// Requires `feature = "luau"`
///
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn coverage<F>(&self, mut func: F)
where
F: FnMut(CoverageInfo),
{
use std::ffi::CStr;
use std::os::raw::c_char;
unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>(
data: *mut c_void,
function: *const c_char,
line_defined: c_int,
depth: c_int,
hits: *const c_int,
size: usize,
) {
let function = if !function.is_null() {
Some(CStr::from_ptr(function).to_string_lossy().to_string())
} else {
None
};
let rust_callback = &mut *(data as *mut F);
rust_callback(CoverageInfo {
function,
line_defined,
depth,
hits: slice::from_raw_parts(hits, size).to_vec(),
});
}
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
lua.push_ref(&self.0);
let func_ptr = &mut func as *mut F as *mut c_void;
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
}
}
/// Converts this function to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Creates a deep clone of the Lua function.
///
/// Copies the function prototype and all its upvalues to the
/// newly created function.
/// This function returns shallow clone (same handle) for Rust/C functions.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn deep_clone(&self) -> Self {
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
return self.clone();
}
ffi::lua_clonefunction(ref_thread, self.0.index);
Function(lua.pop_ref_thread())
}
}
}
struct WrappedFunction(pub(crate) Callback);
#[cfg(feature = "async")]
struct WrappedAsyncFunction(pub(crate) AsyncCallback);
impl Function {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
#[inline]
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
///
/// This function is similar to [`Function::wrap`] but any returned `Result` will be converted
/// to a `ok, err` tuple without throwing an exception.
#[inline]
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeFn<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args).push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
///
/// This function is similar to [`Function::wrap_mut`] but any returned `Result` will be
/// converted to a `ok, err` tuple without throwing an exception.
#[inline]
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeFnMut<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args).push_into_stack_multi(lua)
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let lua = rawlua.lua();
let fut = func.call(args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
///
/// This function is similar to [`Function::wrap_async`] but any returned `Result` will be
/// converted to a `ok, err` tuple without throwing an exception.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let lua = rawlua.lua();
let fut = func.call(args);
Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
}))
}
}
impl IntoLua for WrappedFunction {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
lua.lock().create_callback(self.0).map(Value::Function)
}
}
#[cfg(feature = "async")]
impl IntoLua for WrappedAsyncFunction {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
lua.lock().create_async_callback(self.0).map(Value::Function)
}
}
impl LuaType for Function {
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Function: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Function: Send, Sync);
}
+281 -76
View File
@@ -1,55 +1,143 @@
use std::ffi::CStr;
use std::marker::PhantomData;
use std::os::raw::{c_char, c_int};
use std::borrow::Cow;
use std::cell::UnsafeCell;
use std::ops::Deref;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
use std::os::raw::c_int;
use crate::ffi::{self, lua_Debug, lua_State};
use crate::lua::Lua;
use crate::util::callback_error;
use ffi::lua_Debug;
use crate::state::RawLua;
use crate::types::ReentrantMutexGuard;
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
/// Contains information about currently executing Lua code.
///
/// The `Debug` structure is provided as a parameter to the hook function set with
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
/// Lua code executing at the time that the hook function was called. Further information can be
/// found in the [Lua 5.3 documentation][lua_doc].
/// found in the Lua [documentation].
///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
#[derive(Clone)]
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
/// [`Lua::set_hook`]: crate::Lua::set_hook
pub struct Debug<'a> {
ar: *mut lua_Debug,
state: *mut lua_State,
_phantom: PhantomData<&'a ()>,
lua: EitherLua<'a>,
ar: ActivationRecord,
#[cfg(feature = "luau")]
level: c_int,
}
enum EitherLua<'a> {
Owned(ReentrantMutexGuard<'a, RawLua>),
#[cfg(not(feature = "luau"))]
Borrowed(&'a RawLua),
}
impl Deref for EitherLua<'_> {
type Target = RawLua;
fn deref(&self) -> &Self::Target {
match self {
EitherLua::Owned(guard) => guard,
#[cfg(not(feature = "luau"))]
EitherLua::Borrowed(lua) => lua,
}
}
}
impl<'a> Debug<'a> {
/// Corresponds to the `n` what mask.
pub fn names(&self) -> DebugNames<'a> {
// We assume the lock is held when this function is called.
#[cfg(not(feature = "luau"))]
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
Debug {
lua: EitherLua::Borrowed(lua),
ar: ActivationRecord::Borrowed(ar),
}
}
pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
Debug {
lua: EitherLua::Owned(guard),
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
#[cfg(feature = "luau")]
level: _level,
}
}
/// Returns the specific event that triggered the hook.
///
/// For [Lua 5.1] [`DebugEvent::TailCall`] is used for return events to indicate a return
/// from a function that did a tail call.
///
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn event(&self) -> DebugEvent {
unsafe {
match (*self.ar.get()).event {
ffi::LUA_HOOKCALL => DebugEvent::Call,
ffi::LUA_HOOKRET => DebugEvent::Ret,
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
ffi::LUA_HOOKLINE => DebugEvent::Line,
ffi::LUA_HOOKCOUNT => DebugEvent::Count,
event => DebugEvent::Unknown(event),
}
}
}
/// Corresponds to the `n` what mask.
pub fn names(&self) -> DebugNames {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
DebugNames {
name: ptr_to_str((*self.ar).name),
name_what: ptr_to_str((*self.ar).namewhat),
name: ptr_to_lossy_str((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
Some("") => None,
val => val,
},
#[cfg(feature = "luau")]
name_what: None,
}
}
}
/// Corresponds to the `S` what mask.
pub fn source(&self) -> DebugSource<'a> {
pub fn source(&self) -> DebugSource {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
"lua_getinfo failed with `S`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
"lua_getinfo failed with `s`"
);
DebugSource {
source: ptr_to_str((*self.ar).source),
short_src: ptr_to_str((*self.ar).short_src.as_ptr()),
line_defined: (*self.ar).linedefined as i32,
last_line_defined: (*self.ar).lastlinedefined as i32,
what: ptr_to_str((*self.ar).what),
source: ptr_to_lossy_str((*self.ar.get()).source),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
#[cfg(feature = "luau")]
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
#[cfg(not(feature = "luau"))]
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: None,
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
}
}
}
@@ -57,71 +145,140 @@ impl<'a> Debug<'a> {
/// Corresponds to the `l` what mask. Returns the current line.
pub fn curr_line(&self) -> i32 {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
(*self.ar).currentline as i32
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
(*self.ar.get()).currentline
}
}
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
/// otherwise.
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn is_tail_call(&self) -> bool {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
"lua_getinfo failed with `t`"
);
(*self.ar).currentline != 0
(*self.ar.get()).currentline != 0
}
}
/// Corresponds to the `u` what mask.
pub fn stack(&self) -> DebugStack {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
"lua_getinfo failed with `u`"
);
DebugStack {
num_ups: (*self.ar).nups as i32,
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
"lua_getinfo failed with `au`"
);
#[cfg(not(feature = "luau"))]
let stack = DebugStack {
num_ups: (*self.ar.get()).nups as _,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
num_params: (*self.ar).nparams as i32,
num_params: (*self.ar.get()).nparams as _,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
is_vararg: (*self.ar).isvararg != 0,
}
is_vararg: (*self.ar.get()).isvararg != 0,
};
#[cfg(feature = "luau")]
let stack = DebugStack {
num_ups: (*self.ar.get()).nupvals,
num_params: (*self.ar.get()).nparams,
is_vararg: (*self.ar.get()).isvararg != 0,
};
stack
}
}
}
enum ActivationRecord {
#[cfg(not(feature = "luau"))]
Borrowed(*mut lua_Debug),
Owned(UnsafeCell<lua_Debug>),
}
impl ActivationRecord {
#[inline]
fn get(&self) -> *mut lua_Debug {
match self {
#[cfg(not(feature = "luau"))]
ActivationRecord::Borrowed(x) => *x,
ActivationRecord::Owned(x) => x.get(),
}
}
}
/// Represents a specific event that triggered the hook.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DebugEvent {
Call,
Ret,
TailCall,
Line,
Count,
Unknown(c_int),
}
#[derive(Clone, Debug)]
pub struct DebugNames<'a> {
pub name: Option<&'a [u8]>,
pub name_what: Option<&'a [u8]>,
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<Cow<'a, str>>,
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
}
#[derive(Clone, Debug)]
pub struct DebugSource<'a> {
pub source: Option<&'a [u8]>,
pub short_src: Option<&'a [u8]>,
pub line_defined: i32,
pub last_line_defined: i32,
pub what: Option<&'a [u8]>,
/// Source of the chunk that created the function.
pub source: Option<Cow<'a, str>>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<Cow<'a, str>>,
/// The line number where the definition of the function starts.
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
/// the main part of a chunk.
pub what: &'static str,
}
#[derive(Copy, Clone, Debug)]
pub struct DebugStack {
pub num_ups: i32,
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
pub num_params: i32,
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
/// Number of upvalues.
pub num_ups: u8,
/// Number of parameters.
///
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub num_params: u8,
/// Whether the function is a vararg function.
///
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub is_vararg: bool,
}
/// Determines when a hook function will be called by Lua.
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
#[derive(Clone, Copy, Debug, Default)]
pub struct HookTriggers {
/// Before a function call.
@@ -139,9 +296,61 @@ pub struct HookTriggers {
pub every_nth_instruction: Option<u32>,
}
#[cfg(not(feature = "luau"))]
impl HookTriggers {
/// An instance of `HookTriggers` with `on_calls` trigger set.
pub const ON_CALLS: Self = HookTriggers::new().on_calls();
/// An instance of `HookTriggers` with `on_returns` trigger set.
pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
/// An instance of `HookTriggers` with `every_line` trigger set.
pub const EVERY_LINE: Self = HookTriggers::new().every_line();
/// Returns a new instance of `HookTriggers` with all triggers disabled.
pub const fn new() -> Self {
HookTriggers {
on_calls: false,
on_returns: false,
every_line: false,
every_nth_instruction: None,
}
}
/// Returns an instance of `HookTriggers` with [`on_calls`] trigger set.
///
/// [`on_calls`]: #structfield.on_calls
pub const fn on_calls(mut self) -> Self {
self.on_calls = true;
self
}
/// Returns an instance of `HookTriggers` with [`on_returns`] trigger set.
///
/// [`on_returns`]: #structfield.on_returns
pub const fn on_returns(mut self) -> Self {
self.on_returns = true;
self
}
/// Returns an instance of `HookTriggers` with [`every_line`] trigger set.
///
/// [`every_line`]: #structfield.every_line
pub const fn every_line(mut self) -> Self {
self.every_line = true;
self
}
/// Returns an instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
///
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
pub const fn every_nth_instruction(mut self, n: u32) -> Self {
self.every_nth_instruction = Some(n);
self
}
// Compute the mask to pass to `lua_sethook`.
pub(crate) fn mask(&self) -> c_int {
pub(crate) const fn mask(&self) -> c_int {
let mut mask: c_int = 0;
if self.on_calls {
mask |= ffi::LUA_MASKCALL
@@ -160,36 +369,32 @@ impl HookTriggers {
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
// returned.
pub(crate) fn count(&self) -> c_int {
self.every_nth_instruction.unwrap_or(0) as c_int
pub(crate) const fn count(&self) -> c_int {
match self.every_nth_instruction {
Some(n) => n as c_int,
None => 0,
}
}
}
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
callback_error(state, |_| {
let debug = Debug {
ar,
state,
_phantom: PhantomData,
};
#[cfg(not(feature = "luau"))]
impl BitOr for HookTriggers {
type Output = Self;
let lua = mlua_expect!(Lua::make_from_ptr(state), "cannot make Lua instance");
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
#[allow(clippy::match_wild_err_arm)]
match hook_cb.try_borrow_mut() {
Ok(mut b) => (&mut *b)(&lua, debug),
Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"),
}?;
Ok(())
})
}
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
if input.is_null() {
None
} else {
Some(CStr::from_ptr(input).to_bytes())
fn bitor(mut self, rhs: Self) -> Self::Output {
self.on_calls |= rhs.on_calls;
self.on_returns |= rhs.on_returns;
self.every_line |= rhs.every_line;
if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
self.every_nth_instruction = rhs.every_nth_instruction;
}
self
}
}
#[cfg(not(feature = "luau"))]
impl BitOrAssign for HookTriggers {
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs;
}
}
+143 -64
View File
@@ -10,10 +10,10 @@
//!
//! # Converting data
//!
//! The [`ToLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
//! The [`IntoLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
//! versa. They are implemented for many data structures found in Rust's standard library.
//!
//! For more general conversions, the [`ToLuaMulti`] and [`FromLuaMulti`] traits allow converting
//! For more general conversions, the [`IntoLuaMulti`] and [`FromLuaMulti`] traits allow converting
//! between Rust types and *any number* of Lua values.
//!
//! Most code in `mlua` is generic over implementors of those traits, so in most places the normal
@@ -27,109 +27,121 @@
//!
//! # Serde support
//!
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua values
//! and vice versa using serde. Any user defined data type that implements [`serde::Serialize`] or
//! [`serde::Deserialize`] can be converted.
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua
//! values and vice versa using serde. Any user defined data type that implements
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
//!
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
//! (including [`UserData`]) into Rust values.
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
//! Lua values into Rust values.
//!
//! Requires `feature = "serialize"`.
//!
//! # Async/await support
//!
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
//! [`AsyncThread`] using any runtime (eg. Tokio).
//! The [`Lua::create_async_function`] allows creating non-blocking functions that returns
//! [`Future`]. Lua code with async capabilities can be executed by [`Function::call_async`] family
//! of functions or polling [`AsyncThread`] using any runtime (eg. Tokio).
//!
//! Requires `feature = "async"`.
//!
//! # `Send` requirement
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds `Send` requirement
//! to [`Function`]s and [`UserData`].
//! # `Send` and `Sync` support
//!
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
//! `Send` requirement to Rust functions and [`UserData`] types.
//!
//! In this case [`Lua`] object and their types can be send or used from other threads. Internally
//! access to Lua VM is synchronized using a reentrant mutex that can be locked many times within
//! the same thread.
//!
//! [Lua programming language]: https://www.lua.org/
//! [`Lua`]: struct.Lua.html
//! [executing]: struct.Chunk.html#method.exec
//! [evaluating]: struct.Chunk.html#method.eval
//! [globals]: struct.Lua.html#method.globals
//! [`ToLua`]: trait.ToLua.html
//! [`FromLua`]: trait.FromLua.html
//! [`ToLuaMulti`]: trait.ToLuaMulti.html
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
//! [`Function`]: struct.Function.html
//! [`UserData`]: trait.UserData.html
//! [`UserDataFields`]: trait.UserDataFields.html
//! [`UserDataMethods`]: trait.UserDataMethods.html
//! [`LuaSerdeExt`]: serde/trait.LuaSerdeExt.html
//! [`Value`]: enum.Value.html
//! [`create_async_function`]: struct.Lua.html#method.create_async_function
//! [`call_async`]: struct.Function.html#method.call_async
//! [`AsyncThread`]: struct.AsyncThread.html
//! [`Future`]: ../futures_core/future/trait.Future.html
//! [executing]: crate::Chunk::exec
//! [evaluating]: crate::Chunk::eval
//! [globals]: crate::Lua::globals
//! [`Future`]: std::future::Future
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.6.0")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
#![allow(clippy::ptr_eq)]
#[macro_use]
mod macros;
mod buffer;
mod chunk;
mod conversion;
mod error;
mod ffi;
mod function;
mod hook;
mod lua;
#[cfg(feature = "luau")]
mod luau;
mod memory;
mod multi;
mod scope;
mod state;
mod stdlib;
mod string;
mod table;
mod thread;
mod traits;
mod types;
mod userdata;
mod util;
mod value;
mod vector;
#[doc(hidden)]
pub use crate::ffi::lua_State;
pub mod prelude;
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::Function;
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
pub use crate::multi::Variadic;
pub use bstr::BString;
pub use ffi::{self, lua_CFunction, lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::multi::{MultiValue, Variadic};
pub use crate::scope::Scope;
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
pub use crate::stdlib::StdLib;
pub use crate::string::String;
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
pub use crate::table::{Table, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
pub use crate::traits::{
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
pub use crate::types::{
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
UserDataRefMut, UserDataRegistry,
};
pub use crate::value::{Nil, Value};
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, vector::Vector};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
#[cfg(feature = "serialize")]
#[doc(inline)]
pub use crate::serde::{ser::Options as SerializeOptions, LuaSerdeExt};
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
pub mod prelude;
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub mod serde;
#[cfg(any(feature = "mlua_derive"))]
#[cfg(feature = "mlua_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate mlua_derive;
@@ -139,9 +151,9 @@ extern crate mlua_derive;
/// This macro allows to write Lua code directly in Rust code.
///
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
/// User Rust types needs to implement [`UserData`] or [`ToLua`] traits.
/// User's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
///
/// Captured variables are moved into the chunk.
/// Captured variables are **moved** into the chunk.
///
/// ```
/// use mlua::{Lua, Result, chunk};
@@ -165,24 +177,91 @@ extern crate mlua_derive;
/// (Single quoted strings only work if they contain a single character, since in Rust,
/// `'a'` is a character literal).
///
/// - Using Lua comments `--` is not desirable in **stable** Rust and can have bad side effects.
///
/// This is because procedural macros have Line/Column information available only in
/// **nightly** Rust. Instead, Lua chunks represented as a big single line of code in stable Rust.
///
/// As workaround, Rust comments `//` can be used.
///
/// Other minor limitations:
///
/// - Certain escape codes in string literals.
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
/// - Certain escape codes in string literals don't work. (Specifically: `\a`, `\b`, `\f`, `\v`,
/// `\123` (octal escape codes), `\u`, and `\U`).
///
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
///
/// - The `//` (floor division) operator is unusable, as its start a comment.
///
/// Everything else should work.
///
/// [`AsChunk`]: trait.AsChunk.html
/// [`UserData`]: trait.UserData.html
/// [`ToLua`]: trait.ToLua.html
#[cfg(any(feature = "macros"))]
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::chunk;
#[cfg(any(feature = "module"))]
/// Derive [`FromLua`] for a Rust type.
///
/// Current implementation generate code that takes [`UserData`] value, borrow it (of the Rust type)
/// and clone.
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::FromLua;
/// Registers Lua module entrypoint.
///
/// You can register multiple entrypoints as required.
///
/// ```
/// use mlua::{Lua, Result, Table};
///
/// #[mlua::lua_module]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// let exports = lua.create_table()?;
/// exports.set("hello", "world")?;
/// Ok(exports)
/// }
/// ```
///
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
///
/// You can also pass options to the attribute:
///
/// * name - name of the module, defaults to the name of the function
///
/// ```ignore
/// #[mlua::lua_module(name = "alt_module")]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// ...
/// }
/// ```
///
/// * skip_memory_check - skip memory allocation checks for some operations.
///
/// In module mode, mlua runs in unknown environment and cannot say are there any memory
/// limits or not. As result, some operations that require memory allocation runs in
/// protected mode. Setting this attribute will improve performance of such operations
/// with risk of having uncaught exceptions and memory leaks.
///
/// ```ignore
/// #[mlua::lua_module(skip_memory_check)]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// ...
/// }
/// ```
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
#[cfg(all(feature = "module", feature = "send"))]
compile_error!("`send` feature is not supported in module mode");
pub(crate) mod private {
use super::*;
pub trait Sealed {}
impl Sealed for Error {}
impl<T> Sealed for std::result::Result<T, Error> {}
impl Sealed for Lua {}
impl Sealed for Table {}
impl Sealed for AnyUserData {}
}
-3024
View File
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
use std::ffi::CStr;
use std::os::raw::c_int;
use crate::error::Result;
use crate::state::Lua;
// Since Luau has some missing standard functions, we re-implement them here
impl Lua {
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
let globals = self.globals();
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
// Set `_VERSION` global to include version number
// The environment variable `LUAU_VERSION` set by the build script
if let Some(version) = ffi::luau_version() {
globals.raw_set("_VERSION", format!("Luau {version}"))?;
}
Ok(())
}
pub(crate) fn disable_c_modules(&self) -> Result<()> {
package::disable_dylibs(self);
Ok(())
}
}
unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
let option = CStr::from_ptr(option);
let arg = ffi::luaL_optinteger(state, 2, 0);
match option.to_str() {
Ok("collect") => {
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
0
}
Ok("stop") => {
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
0
}
Ok("restart") => {
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
0
}
Ok("count") => {
let kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0) as ffi::lua_Number;
let kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0) as ffi::lua_Number;
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
1
}
Ok("step") => {
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
ffi::lua_pushboolean(state, res);
1
}
Ok("isrunning") => {
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
ffi::lua_pushboolean(state, res);
1
}
_ => ffi::luaL_error(state, cstr!("collectgarbage called with invalid option")),
}
}
pub(crate) use package::register_package_module;
mod package;
+270
View File
@@ -0,0 +1,270 @@
use std::ffi::CStr;
use std::fmt::Write;
use std::os::raw::c_int;
use std::path::{PathBuf, MAIN_SEPARATOR_STR};
use std::string::String as StdString;
use std::{env, fs};
use crate::chunk::ChunkMode;
use crate::error::Result;
use crate::state::Lua;
use crate::table::Table;
use crate::traits::IntoLua;
use crate::value::Value;
#[cfg(unix)]
use {libloading::Library, rustc_hash::FxHashMap};
//
// Luau package module
//
#[cfg(unix)]
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 3;
#[cfg(all(unix, feature = "module"))]
#[no_mangle]
#[used]
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
// We keep reference to the loaded dylibs in application data
#[cfg(unix)]
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
#[cfg(unix)]
impl std::ops::Deref for LoadedDylibs {
type Target = FxHashMap<PathBuf, Library>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(unix)]
impl std::ops::DerefMut for LoadedDylibs {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
// Create the package table
let package = lua.create_table()?;
// Set `package.path`
let mut search_path = env::var("LUAU_PATH")
.or_else(|_| env::var("LUA_PATH"))
.unwrap_or_default();
if search_path.is_empty() {
search_path = "?.luau;?.lua".to_string();
}
package.raw_set("path", search_path)?;
// Set `package.cpath`
#[cfg(unix)]
{
let mut search_cpath = env::var("LUAU_CPATH")
.or_else(|_| env::var("LUA_CPATH"))
.unwrap_or_default();
if search_cpath.is_empty() {
if cfg!(any(target_os = "macos", target_os = "ios")) {
search_cpath = "?.dylib".to_string();
} else {
search_cpath = "?.so".to_string();
}
}
package.raw_set("cpath", search_cpath)?;
}
// Set `package.loaded` (table with a list of loaded modules)
let loaded = if let Ok(Some(loaded)) = lua.named_registry_value::<Option<Table>>("_LOADED") {
package.raw_set("loaded", &loaded)?;
loaded
} else {
let loaded = lua.create_table()?;
package.raw_set("loaded", &loaded)?;
lua.set_named_registry_value("_LOADED", &loaded)?;
loaded
};
// Set `package.loaders`
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
package.raw_set("loaders", &loaders)?;
#[cfg(unix)]
{
loaders.push(lua.create_function(dylib_loader)?)?;
lua.set_app_data(LoadedDylibs(FxHashMap::default()));
}
lua.set_named_registry_value("_LOADERS", loaders)?;
// Register the module and `require` function in globals
let globals = lua.globals();
globals.raw_set("package", &package)?;
loaded.raw_set("package", package)?;
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
Ok(())
}
#[allow(unused_variables)]
pub(crate) fn disable_dylibs(lua: &Lua) {
// Presence of `LoadedDylibs` in app data is used as a flag
// to check whether binary modules are enabled
#[cfg(unix)]
lua.remove_app_data::<LoadedDylibs>();
}
unsafe extern "C-unwind" fn lua_require(state: *mut ffi::lua_State) -> c_int {
ffi::lua_settop(state, 1);
let name = ffi::luaL_checkstring(state, 1);
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); // _LOADED is at index 2
if ffi::lua_rawgetfield(state, 2, name) != ffi::LUA_TNIL {
return 1; // module is already loaded
}
ffi::lua_pop(state, 1); // remove nil
// load the module
let err_buf = ffi::lua_newuserdata_t(state, StdString::new());
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADERS")); // _LOADERS is at index 3
for i in 1.. {
if ffi::lua_rawgeti(state, -1, i) == ffi::LUA_TNIL {
// no more loaders?
if (*err_buf).is_empty() {
ffi::luaL_error(state, cstr!("module '%s' not found"), name);
} else {
let bytes = (*err_buf).as_bytes();
let extra = ffi::lua_pushlstring(state, bytes.as_ptr() as *const _, bytes.len());
ffi::luaL_error(state, cstr!("module '%s' not found:%s"), name, extra);
}
}
ffi::lua_pushvalue(state, 1); // name arg
ffi::lua_call(state, 1, 2); // call loader
match ffi::lua_type(state, -2) {
ffi::LUA_TFUNCTION => break, // loader found
ffi::LUA_TSTRING => {
// error message
let msg = ffi::lua_tostring(state, -2);
let msg = CStr::from_ptr(msg).to_string_lossy();
_ = write!(&mut *err_buf, "\n\t{msg}");
}
_ => {}
}
ffi::lua_pop(state, 2); // remove both results
}
ffi::lua_pushvalue(state, 1); // name is 1st argument to module loader
ffi::lua_rotate(state, -2, 1); // loader data <-> name
// stack: ...; loader function; module name; loader data
ffi::lua_call(state, 2, 1);
// stack: ...; result from loader function
if ffi::lua_isnil(state, -1) != 0 {
ffi::lua_pop(state, 1);
ffi::lua_pushboolean(state, 1); // use true as result
}
ffi::lua_pushvalue(state, -1); // make copy of entrypoint result
ffi::lua_setfield(state, 2, name); /* _LOADED[name] = returned value */
1
}
/// Searches for the given `name` in the given `path`.
///
/// `path` is a string containing a sequence of templates separated by semicolons.
fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option<PathBuf> {
let mut names = vec![name.replace('.', MAIN_SEPARATOR_STR)];
if try_prefix && name.contains('.') {
let prefix = name.split_once('.').map(|(prefix, _)| prefix).unwrap();
names.push(prefix.to_string());
}
for path in search_path.split(';') {
for name in &names {
let file_path = PathBuf::from(path.replace('?', name));
if let Ok(true) = fs::metadata(&file_path).map(|m| m.is_file()) {
return Some(file_path);
}
}
}
None
}
//
// Module loaders
//
/// Tries to load a lua (text) file
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
let package = {
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
loaded.raw_get::<Table>("package")
}?;
let search_path = package.get::<StdString>("path").unwrap_or_default();
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
match fs::read(&file_path) {
Ok(buf) => {
return lua
.load(buf)
.set_name(format!("={}", file_path.display()))
.set_mode(ChunkMode::Text)
.into_function()
.map(Value::Function);
}
Err(err) => {
return format!("cannot open '{}': {err}", file_path.display()).into_lua(lua);
}
}
}
Ok(Value::Nil)
}
/// Tries to load a dynamic library
#[cfg(unix)]
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
let package = {
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
loaded.raw_get::<Table>("package")
}?;
let search_cpath = package.get::<StdString>("cpath").unwrap_or_default();
let find_symbol = |lib: &Library| unsafe {
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes()) {
return lua.create_c_function(*entry).map(Value::Function);
}
// Try all in one mode
if let Ok(entry) =
lib.get::<ffi::lua_CFunction>(format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes())
{
return lua.create_c_function(*entry).map(Value::Function);
}
"cannot find module entrypoint".into_lua(lua)
};
if let Some(file_path) = package_searchpath(&modname, &search_cpath, true) {
let file_path = file_path.canonicalize()?;
// Load the library and check for symbol
unsafe {
let mut loaded_dylibs = match lua.app_data_mut::<LoadedDylibs>() {
Some(loaded_dylibs) => loaded_dylibs,
None => return "dynamic libraries are disabled in safe mode".into_lua(lua),
};
// Check if it's already loaded
if let Some(lib) = loaded_dylibs.get(&file_path) {
return find_symbol(lib);
}
if let Ok(lib) = Library::new(&file_path) {
// Check version
let mod_version = lib.get::<*const u32>(b"MLUA_LUAU_ABI_VERSION");
let mod_version = mod_version.map(|v| **v).unwrap_or_default();
if mod_version != TARGET_MLUA_LUAU_ABI_VERSION {
let err = format!("wrong module ABI version (expected {TARGET_MLUA_LUAU_ABI_VERSION}, got {mod_version})");
return err.into_lua(lua);
}
let symbol = find_symbol(&lib);
loaded_dylibs.insert(file_path, lib);
return symbol;
}
}
}
Ok(Value::Nil)
}
+26 -6
View File
@@ -10,26 +10,25 @@ macro_rules! bug_msg {
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
as *const ::std::os::raw::c_char
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
};
}
macro_rules! mlua_panic {
($msg:expr) => {
panic!(bug_msg!($msg));
panic!(bug_msg!($msg))
};
($msg:expr,) => {
mlua_panic!($msg);
mlua_panic!($msg)
};
($msg:expr, $($arg:expr),+) => {
panic!(bug_msg!($msg), $($arg),+);
panic!(bug_msg!($msg), $($arg),+)
};
($msg:expr, $($arg:expr),+,) => {
mlua_panic!($msg, $($arg),+);
mlua_panic!($msg, $($arg),+)
};
}
@@ -94,3 +93,24 @@ macro_rules! require_module_feature {
compile_error!("Feature `module` must be enabled in the `mlua` crate");
};
}
macro_rules! protect_lua {
($state:expr, $nargs:expr, $nresults:expr, $f:expr) => {
crate::util::protect_lua_closure($state, $nargs, $nresults, $f)
};
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
use ::std::os::raw::c_int;
unsafe extern "C-unwind" fn do_call($state_inner: *mut ffi::lua_State) -> c_int {
$code;
let nresults = $nresults;
if nresults == ::ffi::LUA_MULTRET {
ffi::lua_gettop($state_inner)
} else {
nresults
}
}
crate::util::protect_lua_call($state, $nargs, do_call)
}};
}
+164
View File
@@ -0,0 +1,164 @@
use std::alloc::{self, Layout};
use std::os::raw::c_void;
use std::ptr;
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
#[repr(C)]
#[derive(Default)]
pub(crate) struct MemoryState {
used_memory: isize,
memory_limit: isize,
// Can be set to temporary ignore the memory limit.
// This is used when calling `lua_pushcfunction` for lua5.1/jit/luau.
ignore_limit: bool,
// Indicates that the memory limit was reached on the last allocation.
#[cfg(feature = "luau")]
limit_reached: bool,
}
impl MemoryState {
#[cfg(feature = "luau")]
#[inline]
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
let mut mem_state = ptr::null_mut();
ffi::lua_getallocf(state, &mut mem_state);
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
mem_state as *mut MemoryState
}
#[cfg(not(feature = "luau"))]
#[rustversion::since(1.85)]
#[inline]
#[allow(clippy::incompatible_msrv)]
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
let mut mem_state = ptr::null_mut();
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
mem_state = ptr::null_mut();
}
mem_state as *mut MemoryState
}
#[cfg(not(feature = "luau"))]
#[rustversion::before(1.85)]
#[inline]
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
let mut mem_state = ptr::null_mut();
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
mem_state = ptr::null_mut();
}
mem_state as *mut MemoryState
}
#[inline]
pub(crate) fn used_memory(&self) -> usize {
self.used_memory as usize
}
#[inline]
pub(crate) fn memory_limit(&self) -> usize {
self.memory_limit as usize
}
#[inline]
pub(crate) fn set_memory_limit(&mut self, limit: usize) -> usize {
let prev_limit = self.memory_limit;
self.memory_limit = limit as isize;
prev_limit as usize
}
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit/luau
// to bypass the memory limit (if set).
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
#[inline]
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
let mem_state = Self::get(state);
if !mem_state.is_null() {
(*mem_state).ignore_limit = true;
f();
(*mem_state).ignore_limit = false;
} else {
f();
}
}
// Does nothing apart from calling `f()`, we don't need to bypass any limits
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
#[inline]
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
f();
}
// Returns `true` if the memory limit was reached on the last memory operation
#[cfg(feature = "luau")]
#[inline]
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
(*Self::get(state)).limit_reached
}
}
unsafe extern "C" fn allocator(
extra: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void {
let mem_state = &mut *(extra as *mut MemoryState);
#[cfg(feature = "luau")]
{
// Reset the flag
mem_state.limit_reached = false;
}
if nsize == 0 {
// Free memory
if !ptr.is_null() {
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
alloc::dealloc(ptr as *mut u8, layout);
mem_state.used_memory -= osize as isize;
}
return ptr::null_mut();
}
// Do not allocate more than isize::MAX
if nsize > isize::MAX as usize {
return ptr::null_mut();
}
// Are we fit to the memory limits?
let mut mem_diff = nsize as isize;
if !ptr.is_null() {
mem_diff -= osize as isize;
}
let mem_limit = mem_state.memory_limit;
let new_used_memory = mem_state.used_memory + mem_diff;
if mem_limit > 0 && new_used_memory > mem_limit && !mem_state.ignore_limit {
#[cfg(feature = "luau")]
{
mem_state.limit_reached = true;
}
return ptr::null_mut();
}
mem_state.used_memory += mem_diff;
if ptr.is_null() {
// Allocate new memory
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
if new_ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
return new_ptr;
}
// Reallocate memory
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
if new_ptr.is_null() {
alloc::handle_alloc_error(old_layout);
}
new_ptr
}
+329 -77
View File
@@ -1,53 +1,212 @@
#![allow(clippy::wrong_self_convention)]
use std::collections::{vec_deque, VecDeque};
use std::iter::FromIterator;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::result::Result as StdResult;
use crate::error::Result;
use crate::lua::Lua;
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
use crate::state::{Lua, RawLua};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::util::check_stack;
use crate::value::{Nil, Value};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// Result is convertible to [`MultiValue`] following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::new();
impl<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
match self {
Ok(v) => result.push_front(v.to_lua(lua)?),
Err(e) => {
result.push_front(e.to_lua(lua)?);
result.push_front(Nil);
}
Ok(val) => (val,).into_lua_multi(lua),
Err(err) => (Nil, err).into_lua_multi(lua),
}
}
Ok(result)
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
match self {
Ok(val) => (val,).push_into_stack_multi(lua),
Err(err) => (Nil, err).push_into_stack_multi(lua),
}
}
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new();
v.push_front(self.to_lua(lua)?);
impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
match self {
Ok(_) => const { Ok(MultiValue::new()) },
Err(err) => (Nil, err).into_lua_multi(lua),
}
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
match self {
Ok(_) => Ok(0),
Err(err) => (Nil, err).push_into_stack_multi(lua),
}
}
}
impl<T: IntoLua> IntoLuaMulti for T {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let mut v = MultiValue::with_capacity(1);
v.push_back(self.into_lua(lua)?);
Ok(v)
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
self.push_into_stack(lua)?;
Ok(1)
}
}
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
impl<T: FromLua> FromLuaMulti for T {
#[inline]
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
}
#[inline]
fn from_lua_args(mut args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
}
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
if nvals == 0 {
return T::from_lua(Nil, lua.lua());
}
T::from_stack(-nvals, lua)
}
#[inline]
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
if nargs == 0 {
return T::from_lua_arg(Nil, i, to, lua.lua());
}
T::from_stack_arg(-nargs, i, to, lua)
}
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Default, Debug, Clone)]
pub struct MultiValue(VecDeque<Value>);
impl Deref for MultiValue {
type Target = VecDeque<Value>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MultiValue {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl MultiValue {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub const fn new() -> MultiValue {
MultiValue(VecDeque::new())
}
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
pub fn with_capacity(capacity: usize) -> MultiValue {
MultiValue(VecDeque::with_capacity(capacity))
}
/// Creates a `MultiValue` container from vector of values.
///
/// This method works in *O*(1) time and does not allocate any additional memory.
#[inline]
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
vec.into()
}
/// Consumes the `MultiValue` and returns a vector of values.
///
/// This method needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
/// beginning of the allocation.
#[inline]
pub fn into_vec(self) -> Vec<Value> {
self.into()
}
#[inline]
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
let iter = iter.into_iter();
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
for value in iter {
multi_value.push_back(value.into_lua(lua)?);
}
Ok(multi_value)
}
}
impl From<Vec<Value>> for MultiValue {
#[inline]
fn from(value: Vec<Value>) -> Self {
MultiValue(value.into())
}
}
impl From<MultiValue> for Vec<Value> {
#[inline]
fn from(value: MultiValue) -> Self {
value.0.into()
}
}
impl FromIterator<Value> for MultiValue {
#[inline]
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let mut multi_value = MultiValue::new();
multi_value.extend(iter);
multi_value
}
}
impl IntoIterator for MultiValue {
type Item = Value;
type IntoIter = vec_deque::IntoIter<Value>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let deque = mem::take(&mut self.0);
mem::forget(self);
deque.into_iter()
}
}
impl<'a> IntoIterator for &'a MultiValue {
type Item = &'a Value;
type IntoIter = vec_deque::Iter<'a, Value>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoLuaMulti for MultiValue {
#[inline]
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
Ok(self)
}
}
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
impl FromLuaMulti for MultiValue {
#[inline]
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
Ok(values)
}
}
@@ -75,22 +234,46 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
/// # Ok(())
/// # }
/// ```
///
/// [`FromLua`]: trait.FromLua.html
/// [`MultiValue`]: struct.MultiValue.html
#[derive(Debug, Clone)]
#[derive(Default, Debug, Clone)]
pub struct Variadic<T>(Vec<T>);
impl<T> Variadic<T> {
/// Creates an empty `Variadic` wrapper containing no values.
pub fn new() -> Variadic<T> {
pub const fn new() -> Variadic<T> {
Variadic(Vec::new())
}
/// Creates an empty `Variadic` container with space for at least `capacity` elements.
pub fn with_capacity(capacity: usize) -> Variadic<T> {
Variadic(Vec::with_capacity(capacity))
}
}
impl<T> Default for Variadic<T> {
fn default() -> Variadic<T> {
Variadic::new()
impl<T> Deref for Variadic<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Variadic<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<Vec<T>> for Variadic<T> {
#[inline]
fn from(vec: Vec<T>) -> Self {
Variadic(vec)
}
}
impl<T> From<Variadic<T>> for Vec<T> {
#[inline]
fn from(value: Variadic<T>) -> Self {
value.0
}
}
@@ -109,31 +292,19 @@ impl<T> IntoIterator for Variadic<T> {
}
}
impl<T> Deref for Variadic<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
MultiValue::from_lua_iter(lua, self)
}
}
impl<T> DerefMut for Variadic<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
self.0.into_iter().map(|e| e.to_lua(lua)).collect()
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
impl<T: FromLua> FromLuaMulti for Variadic<T> {
#[inline]
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
values
.into_iter()
.map(|e| T::from_lua(e, lua))
.drain(..)
.map(|val| T::from_lua(val, lua))
.collect::<Result<Vec<T>>>()
.map(Variadic)
}
@@ -141,45 +312,116 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
macro_rules! impl_tuple {
() => (
impl<'lua> ToLuaMulti<'lua> for () {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new())
impl IntoLuaMulti for () {
#[inline]
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
const { Ok(MultiValue::new()) }
}
#[inline]
unsafe fn push_into_stack_multi(self, _lua: &RawLua) -> Result<c_int> {
Ok(0)
}
}
impl<'lua> FromLuaMulti<'lua> for () {
fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
impl FromLuaMulti for () {
#[inline]
fn from_lua_multi(_values: MultiValue, _lua: &Lua) -> Result<Self> {
Ok(())
}
#[inline]
unsafe fn from_stack_multi(_nvals: c_int, _lua: &RawLua) -> Result<Self> {
Ok(())
}
}
);
($last:ident $($name:ident)*) => (
impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,)
where $($name: ToLua<'lua>,)*
$last: ToLuaMulti<'lua>
impl<$($name,)* $last> IntoLuaMulti for ($($name,)* $last,)
where $($name: IntoLua,)*
$last: IntoLuaMulti
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
#[allow(unused_mut, non_snake_case)]
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let ($($name,)* $last,) = self;
let mut results = $last.to_lua_multi(lua)?;
push_reverse!(results, $($name.to_lua(lua)?,)*);
let mut results = $last.into_lua_multi(lua)?;
push_reverse!(results, $($name.into_lua(lua)?,)*);
Ok(results)
}
#[allow(non_snake_case)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let ($($name,)* $last,) = self;
let mut nresults = 0;
$(
_ = $name;
nresults += 1;
)*
check_stack(lua.state(), nresults + 1)?;
$(
$name.push_into_stack(lua)?;
)*
nresults += $last.push_into_stack_multi(lua)?;
Ok(nresults)
}
}
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
where $($name: FromLua<'lua>,)*
$last: FromLuaMulti<'lua>
impl<$($name,)* $last> FromLuaMulti for ($($name,)* $last,)
where $($name: FromLua,)*
$last: FromLuaMulti
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
$(let $name = values.pop_front().unwrap_or(Nil);)*
#[allow(unused_mut, non_snake_case)]
#[inline]
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
Ok(($(FromLua::from_lua($name, lua)?,)* $last,))
Ok(($($name,)* $last,))
}
#[allow(unused_mut, non_snake_case)]
#[inline]
fn from_lua_args(mut args: MultiValue, mut i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
$(
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
i += 1;
)*
let $last = FromLuaMulti::from_lua_args(args, i, to, lua)?;
Ok(($($name,)* $last,))
}
#[allow(unused_mut, non_snake_case)]
#[inline]
unsafe fn from_stack_multi(mut nvals: c_int, lua: &RawLua) -> Result<Self> {
$(
let $name = if nvals > 0 {
nvals -= 1;
FromLua::from_stack(-(nvals + 1), lua)
} else {
FromLua::from_lua(Nil, lua.lua())
}?;
)*
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
Ok(($($name,)* $last,))
}
#[allow(unused_mut, non_snake_case)]
#[inline]
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
$(
let $name = if nargs > 0 {
nargs -= 1;
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
} else {
FromLua::from_lua_arg(Nil, i, to, lua.lua())
}?;
i += 1;
)*
let $last = FromLuaMulti::from_stack_args(nargs, i, to, lua)?;
Ok(($($name,)* $last,))
}
}
);
@@ -215,3 +457,13 @@ impl_tuple!(A B C D E F G H I J K L M);
impl_tuple!(A B C D E F G H I J K L M N);
impl_tuple!(A B C D E F G H I J K L M N O);
impl_tuple!(A B C D E F G H I J K L M N O P);
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(MultiValue: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
}
+27 -13
View File
@@ -1,21 +1,35 @@
//! Re-exports most types with an extra `Lua*` prefix to prevent name clashes.
#[doc(no_inline)]
pub use crate::{
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Either as LuaEither, Error as LuaError,
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn,
LuaNativeFnMut, LuaOptions, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil,
Number as LuaNumber, ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult,
StdLib as LuaStdLib, String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs,
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
};
#[cfg(not(feature = "luau"))]
#[doc(no_inline)]
pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector};
#[cfg(feature = "async")]
pub use crate::AsyncThread as LuaAsyncThread;
#[doc(no_inline)]
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
#[cfg(feature = "serialize")]
#[doc(inline)]
pub use crate::{LuaSerdeExt, SerializeOptions as LuaSerializeOptions};
#[doc(no_inline)]
pub use crate::{
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
};
+241 -749
View File
File diff suppressed because it is too large Load Diff
+487 -81
View File
@@ -1,23 +1,140 @@
//! Deserialize Lua values to a Rust data structure.
use std::cell::RefCell;
use std::os::raw::c_void;
use std::rc::Rc;
use std::result::Result as StdResult;
use std::string::String as StdString;
use rustc_hash::FxHashSet;
use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result};
use crate::table::{TablePairs, TableSequence};
use crate::table::{Table, TablePairs, TableSequence};
use crate::userdata::AnyUserData;
use crate::value::Value;
/// A struct for deserializing Lua values into Rust values.
#[derive(Debug)]
pub struct Deserializer<'lua>(Value<'lua>);
pub struct Deserializer {
value: Value,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua> Deserializer<'lua> {
/// Creates a new Lua Deserializer for the `Value`.
pub fn new(value: Value<'lua>) -> Self {
Deserializer(value)
/// A struct with options to change default deserializer behavior.
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct Options {
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
/// and [`Error`] will cause an error.
/// Otherwise these types skipped when iterating or serialized as unit type.
///
/// Default: **true**
///
/// [`Function`]: crate::Function
/// [`Thread`]: crate::Thread
/// [`LightUserData`]: crate::LightUserData
/// [`Error`]: crate::Error
pub deny_unsupported_types: bool,
/// If true, an attempt to serialize a recursive table (table that refers to itself)
/// will cause an error.
/// Otherwise subsequent attempts to serialize the same table will be ignored.
///
/// Default: **true**
pub deny_recursive_tables: bool,
/// If true, keys in tables will be iterated in sorted order.
///
/// Default: **false**
pub sort_keys: bool,
/// If true, empty Lua tables will be encoded as array, instead of map.
///
/// Default: **false**
pub encode_empty_tables_as_array: bool,
}
impl Default for Options {
fn default() -> Self {
const { Self::new() }
}
}
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
impl Options {
/// Returns a new instance of `Options` with default parameters.
pub const fn new() -> Self {
Options {
deny_unsupported_types: true,
deny_recursive_tables: true,
sort_keys: false,
encode_empty_tables_as_array: false,
}
}
/// Sets [`deny_unsupported_types`] option.
///
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
#[must_use]
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
self.deny_unsupported_types = enabled;
self
}
/// Sets [`deny_recursive_tables`] option.
///
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
#[must_use]
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
self.deny_recursive_tables = enabled;
self
}
/// Sets [`sort_keys`] option.
///
/// [`sort_keys`]: #structfield.sort_keys
#[must_use]
pub const fn sort_keys(mut self, enabled: bool) -> Self {
self.sort_keys = enabled;
self
}
/// Sets [`encode_empty_tables_as_array`] option.
///
/// [`encode_empty_tables_as_array`]: #structfield.encode_empty_tables_as_array
#[must_use]
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
self.encode_empty_tables_as_array = enabled;
self
}
}
impl Deserializer {
/// Creates a new Lua Deserializer for the [`Value`].
pub fn new(value: Value) -> Self {
Self::new_with_options(value, Options::default())
}
/// Creates a new Lua Deserializer for the [`Value`] with custom options.
pub fn new_with_options(value: Value, options: Options) -> Self {
Deserializer {
value,
options,
visited: Rc::new(RefCell::new(FxHashSet::default())),
}
}
fn from_parts(value: Value, options: Options, visited: Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
Deserializer {
value,
options,
visited,
}
}
}
impl<'de> serde::Deserializer<'de> for Deserializer {
type Error = Error;
#[inline]
@@ -25,25 +142,43 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
where
V: de::Visitor<'de>,
{
match self.0 {
match self.value {
Value::Nil => visitor.visit_unit(),
Value::Boolean(b) => visitor.visit_bool(b),
#[allow(clippy::useless_conversion)]
Value::Integer(i) => visitor.visit_i64(i.into()),
#[allow(clippy::useless_conversion)]
Value::Number(n) => visitor.visit_f64(n.into()),
#[cfg(feature = "luau")]
Value::Vector(_) => self.deserialize_seq(visitor),
Value::String(s) => match s.to_str() {
Ok(s) => visitor.visit_str(s),
Err(_) => visitor.visit_bytes(s.as_bytes()),
Ok(s) => visitor.visit_str(&s),
Err(_) => visitor.visit_bytes(&s.as_bytes()),
},
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
Value::Table(ref t) if self.options.encode_empty_tables_as_array && t.is_empty() => {
self.deserialize_seq(visitor)
}
Value::Table(_) => self.deserialize_map(visitor),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_any(visitor))
}
#[cfg(feature = "luau")]
Value::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => Err(de::Error::custom("invalid value type")),
| Value::Error(_)
| Value::Other(_) => {
if self.options.deny_unsupported_types {
let msg = format!("unsupported value type `{}`", self.value.type_name());
Err(de::Error::custom(msg))
} else {
visitor.visit_unit()
}
}
}
}
@@ -52,7 +187,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
where
V: de::Visitor<'de>,
{
match self.0 {
match self.value {
Value::Nil => visitor.visit_none(),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
_ => visitor.visit_some(self),
@@ -62,16 +197,18 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[inline]
fn deserialize_enum<V>(
self,
_name: &str,
_variants: &'static [&'static str],
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let (variant, value) = match self.0 {
Value::Table(value) => {
let mut iter = value.pairs::<StdString, Value>();
let (variant, value, _guard) = match self.value {
Value::Table(table) => {
let _guard = RecursionGuard::new(&table, &self.visited);
let mut iter = table.pairs::<StdString, Value>();
let (variant, value) = match iter.next() {
Some(v) => v?,
None => {
@@ -88,13 +225,27 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
&"map with a single key",
));
}
(variant, Some(value))
let skip = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
if skip {
return Err(de::Error::custom("bad enum value"));
}
(variant, Some(value), Some(_guard))
}
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
Value::UserData(ud) if ud.is_serializable() => {
return serde_userdata(ud, |value| value.deserialize_enum(name, variants, visitor));
}
Value::String(variant) => (variant.to_str()?.to_owned(), None),
_ => return Err(de::Error::custom("bad enum value")),
};
visitor.visit_enum(EnumDeserializer { variant, value })
visitor.visit_enum(EnumDeserializer {
variant,
value,
options: self.options,
visited: self.visited,
})
}
#[inline]
@@ -102,21 +253,40 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
where
V: de::Visitor<'de>,
{
match self.0 {
match self.value {
#[cfg(feature = "luau")]
Value::Vector(vec) => {
let mut deserializer = VecDeserializer {
vec,
next: 0,
options: self.options,
visited: self.visited,
};
visitor.visit_seq(&mut deserializer)
}
Value::Table(t) => {
let len = t.raw_len() as usize;
let mut deserializer = SeqDeserializer(t.raw_sequence_values());
let _guard = RecursionGuard::new(&t, &self.visited);
let len = t.raw_len();
let mut deserializer = SeqDeserializer {
seq: t.sequence_values(),
options: self.options,
visited: self.visited,
};
let seq = visitor.visit_seq(&mut deserializer)?;
if deserializer.0.count() == 0 {
if deserializer.seq.count() == 0 {
Ok(seq)
} else {
Err(de::Error::invalid_length(
len,
&"fewer elements in the table",
))
Err(de::Error::invalid_length(len, &"fewer elements in the table"))
}
}
_ => Err(de::Error::custom("invalid value type")),
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_seq(visitor))
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
)),
}
}
@@ -129,12 +299,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
#[inline]
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value>
fn deserialize_tuple_struct<V>(self, _name: &'static str, _len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
@@ -146,9 +311,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
where
V: de::Visitor<'de>,
{
match self.0 {
match self.value {
Value::Table(t) => {
let mut deserializer = MapDeserializer::new(t.pairs());
let _guard = RecursionGuard::new(&t, &self.visited);
let mut deserializer = MapDeserializer {
pairs: MapPairs::new(&t, self.options.sort_keys)?,
value: None,
options: self.options,
visited: self.visited,
processed: 0,
};
let map = visitor.visit_map(&mut deserializer)?;
let count = deserializer.pairs.count();
if count == 0 {
@@ -160,7 +333,13 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
))
}
}
_ => Err(de::Error::custom("invalid value type")),
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_map(visitor))
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
)),
}
}
@@ -177,68 +356,214 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
self.deserialize_map(visitor)
}
#[inline]
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_newtype_struct(name, visitor))
}
_ => visitor.visit_newtype_struct(self),
}
}
#[inline]
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
#[inline]
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
byte_buf unit unit_struct newtype_struct
identifier ignored_any
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf identifier ignored_any
}
}
struct SeqDeserializer<'lua>(TableSequence<'lua, Value<'lua>>);
struct SeqDeserializer<'a> {
seq: TableSequence<'a, Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
match self.0.next() {
Some(value) => seed.deserialize(Deserializer(value?)).map(Some),
None => Ok(None),
loop {
match self.seq.next() {
Some(value) => {
let value = value?;
let skip = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
if skip {
continue;
}
let visited = Rc::clone(&self.visited);
let deserializer = Deserializer::from_parts(value, self.options, visited);
return seed.deserialize(deserializer).map(Some);
}
None => return Ok(None),
}
}
}
fn size_hint(&self) -> Option<usize> {
match self.0.size_hint() {
match self.seq.size_hint() {
(lower, Some(upper)) if lower == upper => Some(upper),
_ => None,
}
}
}
struct MapDeserializer<'lua> {
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
value: Option<Value<'lua>>,
processed: usize,
#[cfg(feature = "luau")]
struct VecDeserializer {
vec: crate::Vector,
next: usize,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua> MapDeserializer<'lua> {
fn new(pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>) -> Self {
MapDeserializer {
pairs,
value: None,
processed: 0,
#[cfg(feature = "luau")]
impl<'de> de::SeqAccess<'de> for VecDeserializer {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
match self.vec.0.get(self.next) {
Some(&n) => {
self.next += 1;
let visited = Rc::clone(&self.visited);
let deserializer = Deserializer::from_parts(Value::Number(n as _), self.options, visited);
seed.deserialize(deserializer).map(Some)
}
None => Ok(None),
}
}
fn size_hint(&self) -> Option<usize> {
Some(crate::Vector::SIZE)
}
}
pub(crate) enum MapPairs<'a> {
Iter(TablePairs<'a, Value, Value>),
Vec(Vec<(Value, Value)>),
}
impl<'a> MapPairs<'a> {
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
if sort_keys {
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
pairs.sort_by(|(a, _), (b, _)| b.sort_cmp(a)); // reverse order as we pop values from the end
Ok(MapPairs::Vec(pairs))
} else {
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
}
}
pub(crate) fn count(self) -> usize {
match self {
MapPairs::Iter(iter) => iter.count(),
MapPairs::Vec(vec) => vec.len(),
}
}
pub(crate) fn size_hint(&self) -> (usize, Option<usize>) {
match self {
MapPairs::Iter(iter) => iter.size_hint(),
MapPairs::Vec(vec) => (vec.len(), Some(vec.len())),
}
}
}
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
impl Iterator for MapPairs<'_> {
type Item = Result<(Value, Value)>;
fn next(&mut self) -> Option<Self::Item> {
match self {
MapPairs::Iter(iter) => iter.next(),
MapPairs::Vec(vec) => vec.pop().map(Ok),
}
}
}
struct MapDeserializer<'a> {
pairs: MapPairs<'a>,
value: Option<Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
processed: usize,
}
impl MapDeserializer<'_> {
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
loop {
match self.pairs.next() {
Some(item) => {
let (key, value) = item?;
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::DeserializeError(err.to_string()))?;
if skip_key || skip_value {
continue;
}
self.processed += 1;
self.value = Some(value);
let visited = Rc::clone(&self.visited);
let key_de = Deserializer::from_parts(key, self.options, visited);
return Ok(Some(key_de));
}
None => return Ok(None),
}
}
}
fn next_value_deserializer(&mut self) -> Result<Deserializer> {
match self.value.take() {
Some(value) => {
let visited = Rc::clone(&self.visited);
Ok(Deserializer::from_parts(value, self.options, visited))
}
None => Err(de::Error::custom("value is missing")),
}
}
}
impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
type Error = Error;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
match self.pairs.next() {
Some(item) => {
let (key, value) = item?;
self.processed += 1;
self.value = Some(value);
let key_de = Deserializer(key);
seed.deserialize(key_de).map(Some)
}
None => Ok(None),
match self.next_key_deserializer() {
Ok(Some(key_de)) => seed.deserialize(key_de).map(Some),
Ok(None) => Ok(None),
Err(error) => Err(error),
}
}
@@ -246,9 +571,9 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
where
T: de::DeserializeSeed<'de>,
{
match self.value.take() {
Some(value) => seed.deserialize(Deserializer(value)),
None => Err(de::Error::custom("value is missing")),
match self.next_value_deserializer() {
Ok(value_de) => seed.deserialize(value_de),
Err(error) => Err(error),
}
}
@@ -260,30 +585,38 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
}
}
struct EnumDeserializer<'lua> {
struct EnumDeserializer {
variant: StdString,
value: Option<Value<'lua>>,
value: Option<Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
impl<'de> de::EnumAccess<'de> for EnumDeserializer {
type Error = Error;
type Variant = VariantDeserializer<'lua>;
type Variant = VariantDeserializer;
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant)>
where
T: de::DeserializeSeed<'de>,
{
let variant = self.variant.into_deserializer();
let variant_access = VariantDeserializer { value: self.value };
let variant_access = VariantDeserializer {
value: self.value,
options: self.options,
visited: self.visited,
};
seed.deserialize(variant).map(|v| (v, variant_access))
}
}
struct VariantDeserializer<'lua> {
value: Option<Value<'lua>>,
struct VariantDeserializer {
value: Option<Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
impl<'de> de::VariantAccess<'de> for VariantDeserializer {
type Error = Error;
fn unit_variant(self) -> Result<()> {
@@ -301,7 +634,7 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
T: de::DeserializeSeed<'de>,
{
match self.value {
Some(value) => seed.deserialize(Deserializer(value)),
Some(value) => seed.deserialize(Deserializer::from_parts(value, self.options, self.visited)),
None => Err(de::Error::invalid_type(
de::Unexpected::UnitVariant,
&"newtype variant",
@@ -314,7 +647,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
V: de::Visitor<'de>,
{
match self.value {
Some(value) => serde::Deserializer::deserialize_seq(Deserializer(value), visitor),
Some(value) => serde::Deserializer::deserialize_seq(
Deserializer::from_parts(value, self.options, self.visited),
visitor,
),
None => Err(de::Error::invalid_type(
de::Unexpected::UnitVariant,
&"tuple variant",
@@ -327,7 +663,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
V: de::Visitor<'de>,
{
match self.value {
Some(value) => serde::Deserializer::deserialize_map(Deserializer(value), visitor),
Some(value) => serde::Deserializer::deserialize_map(
Deserializer::from_parts(value, self.options, self.visited),
visitor,
),
None => Err(de::Error::invalid_type(
de::Unexpected::UnitVariant,
&"struct variant",
@@ -335,3 +674,70 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
}
}
}
// Adds `ptr` to the `visited` map and removes on drop
// Used to track recursive tables but allow to traverse same tables multiple times
pub(crate) struct RecursionGuard {
ptr: *const c_void,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl RecursionGuard {
#[inline]
pub(crate) fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
let visited = Rc::clone(visited);
let ptr = table.to_pointer();
visited.borrow_mut().insert(ptr);
RecursionGuard { ptr, visited }
}
}
impl Drop for RecursionGuard {
fn drop(&mut self) {
self.visited.borrow_mut().remove(&self.ptr);
}
}
// Checks `options` and decides should we emit an error or skip next element
pub(crate) fn check_value_for_skip(
value: &Value,
options: Options,
visited: &RefCell<FxHashSet<*const c_void>>,
) -> StdResult<bool, &'static str> {
match value {
Value::Table(table) => {
let ptr = table.to_pointer();
if visited.borrow().contains(&ptr) {
if options.deny_recursive_tables {
return Err("recursive table detected");
}
return Ok(true); // skip
}
}
Value::UserData(ud) if ud.is_serializable() => {}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_)
if !options.deny_unsupported_types =>
{
return Ok(true); // skip
}
_ => {}
}
Ok(false) // do not skip
}
fn serde_userdata<V>(
ud: AnyUserData,
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
) -> Result<V> {
match serde_value::to_value(ud) {
Ok(value) => match f(value) {
Ok(r) => Ok(r),
Err(error) => Err(Error::DeserializeError(error.to_string())),
},
Err(error) => Err(Error::SerializeError(error.to_string())),
}
}
+69 -38
View File
@@ -1,20 +1,20 @@
//! (De)Serialization support using serde.
use std::os::raw::c_void;
use std::ptr;
use serde::{Deserialize, Serialize};
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::private::Sealed;
use crate::state::Lua;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
use crate::util::check_stack;
use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
pub trait LuaSerdeExt<'lua> {
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub trait LuaSerdeExt: Sealed {
/// A special value (lightuserdata) to encode/decode optional (none) values.
///
/// Requires `feature = "serialize"`
@@ -36,7 +36,7 @@ pub trait LuaSerdeExt<'lua> {
/// Ok(())
/// }
/// ```
fn null(&'lua self) -> Value<'lua>;
fn null(&self) -> Value;
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
/// As result, encoded Array will contain only sequence part of the table, with the same length
@@ -67,13 +67,13 @@ pub trait LuaSerdeExt<'lua> {
/// Ok(())
/// }
/// ```
fn array_metatable(&'lua self) -> Table<'lua>;
fn array_metatable(&self) -> Table;
/// Converts `T` into a `Value` instance.
/// Converts `T` into a [`Value`] instance.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: enum.Value.html
/// [`Value`]: crate::Value
///
/// # Example
///
@@ -100,14 +100,12 @@ pub trait LuaSerdeExt<'lua> {
/// "#).exec()
/// }
/// ```
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
fn to_value<T: Serialize + ?Sized>(&self, t: &T) -> Result<Value>;
/// Converts `T` into a `Value` instance with options.
/// Converts `T` into a [`Value`] instance with options.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: enum.Value.html
///
/// # Example
///
/// ```
@@ -125,16 +123,14 @@ pub trait LuaSerdeExt<'lua> {
/// "#).exec()
/// }
/// ```
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
where
T: Serialize + ?Sized;
/// Deserializes a `Value` into any serde deserializable object.
/// Deserializes a [`Value`] into any serde deserializable object.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: enum.Value.html
///
/// # Example
///
/// ```
@@ -157,51 +153,86 @@ pub trait LuaSerdeExt<'lua> {
/// Ok(())
/// }
/// ```
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
#[allow(clippy::wrong_self_convention)]
fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
/// Deserializes a [`Value`] into any serde deserializable object with options.
///
/// Requires `feature = "serialize"`
///
/// # Example
///
/// ```
/// use mlua::{Lua, Result, LuaSerdeExt, DeserializeOptions};
/// use serde::Deserialize;
///
/// #[derive(Deserialize, Debug, PartialEq)]
/// struct User {
/// name: String,
/// age: u8,
/// }
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// let val = lua.load(r#"{name = "John Smith", age = 20, f = function() end}"#).eval()?;
/// let options = DeserializeOptions::new().deny_unsupported_types(false);
/// let u: User = lua.from_value_with(val, options)?;
///
/// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
///
/// Ok(())
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options) -> Result<T>;
}
impl<'lua> LuaSerdeExt<'lua> for Lua {
fn null(&'lua self) -> Value<'lua> {
Value::LightUserData(LightUserData(ptr::null_mut()))
impl LuaSerdeExt for Lua {
fn null(&self) -> Value {
Value::NULL
}
fn array_metatable(&'lua self) -> Table<'lua> {
fn array_metatable(&self) -> Table {
let lua = self.lock();
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1);
push_array_metatable(self.state);
Table(self.pop_ref())
push_array_metatable(lua.ref_thread());
Table(lua.pop_ref_thread())
}
}
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
fn to_value<T>(&self, t: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new(self))
}
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new_with_options(self, options))
}
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
fn from_value<T>(&self, value: Value) -> Result<T>
where
T: Deserialize<'lua>,
T: DeserializeOwned,
{
T::deserialize(de::Deserializer::new(value))
}
fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
where
T: DeserializeOwned,
{
T::deserialize(de::Deserializer::new_with_options(value, options))
}
}
// Uses 6 stack spaces and calls checkstack.
// Uses 2 stack spaces and calls checkstack.
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
check_stack(state, 3)?;
protect_lua(state, 0, 0, |state| {
check_stack(state, 2)?;
protect_lua!(state, 0, 0, fn(state) {
ffi::lua_createtable(state, 0, 1);
ffi::lua_pushstring(state, cstr!("__metatable"));
@@ -214,7 +245,7 @@ pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
}
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
}
+232 -136
View File
@@ -1,21 +1,18 @@
use std::os::raw::c_int;
//! Serialize a Rust data structure into Lua value.
use serde::{ser, Serialize};
use super::LuaSerdeExt;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::string::String;
use crate::state::Lua;
use crate::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, protect_lua, StackGuard};
use crate::value::{ToLua, Value};
use crate::traits::IntoLua;
use crate::value::Value;
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
pub struct Serializer<'lua> {
lua: &'lua Lua,
pub struct Serializer<'a> {
lua: &'a Lua,
options: Options,
}
@@ -28,16 +25,16 @@ pub struct Options {
///
/// Default: **true**
///
/// [`array_metatable`]: ../trait.LuaSerdeExt.html#tymethod.array_metatable
/// [`array_metatable`]: crate::LuaSerdeExt::array_metatable
pub set_array_metatable: bool,
/// If true, serialize `None` (part of `Option` type) to [`null`].
/// If true, serialize `None` (part of the `Option` type) to [`null`].
/// Otherwise it will be set to Lua [`Nil`].
///
/// Default: **true**
///
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
/// [`Nil`]: ../../enum.Value.html#variant.Nil
/// [`null`]: crate::LuaSerdeExt::null
/// [`Nil`]: crate::Value::Nil
pub serialize_none_to_null: bool,
/// If true, serialize `Unit` (type of `()` in Rust) and Unit structs to [`null`].
@@ -45,31 +42,39 @@ pub struct Options {
///
/// Default: **true**
///
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
/// [`Nil`]: ../../enum.Value.html#variant.Nil
/// [`null`]: crate::LuaSerdeExt::null
/// [`Nil`]: crate::Value::Nil
pub serialize_unit_to_null: bool,
/// If true, serialize `serde_json::Number` with arbitrary_precision to a Lua number.
/// Otherwise it will be serialized as an object (what serde does).
///
/// Default: **false**
pub detect_serde_json_arbitrary_precision: bool,
}
impl Default for Options {
fn default() -> Self {
Options {
set_array_metatable: true,
serialize_none_to_null: true,
serialize_unit_to_null: true,
}
const { Self::new() }
}
}
impl Options {
/// Retruns a new instance of `Options` with default parameters.
pub fn new() -> Self {
Self::default()
/// Returns a new instance of [`Options`] with default parameters.
pub const fn new() -> Self {
Options {
set_array_metatable: true,
serialize_none_to_null: true,
serialize_unit_to_null: true,
detect_serde_json_arbitrary_precision: false,
}
}
/// Sets [`set_array_metatable`] option.
///
/// [`set_array_metatable`]: #structfield.set_array_metatable
pub fn set_array_metatable(mut self, enabled: bool) -> Self {
#[must_use]
pub const fn set_array_metatable(mut self, enabled: bool) -> Self {
self.set_array_metatable = enabled;
self
}
@@ -77,7 +82,8 @@ impl Options {
/// Sets [`serialize_none_to_null`] option.
///
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
pub fn serialize_none_to_null(mut self, enabled: bool) -> Self {
#[must_use]
pub const fn serialize_none_to_null(mut self, enabled: bool) -> Self {
self.serialize_none_to_null = enabled;
self
}
@@ -85,20 +91,35 @@ impl Options {
/// Sets [`serialize_unit_to_null`] option.
///
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
pub fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
#[must_use]
pub const fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
self.serialize_unit_to_null = enabled;
self
}
/// Sets [`detect_serde_json_arbitrary_precision`] option.
///
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua
/// number. Otherwise it will be serialized as an object (what serde does).
///
/// This option is disabled by default.
///
/// [`detect_serde_json_arbitrary_precision`]: #structfield.detect_serde_json_arbitrary_precision
#[must_use]
pub const fn detect_serde_json_arbitrary_precision(mut self, enabled: bool) -> Self {
self.detect_serde_json_arbitrary_precision = enabled;
self
}
}
impl<'lua> Serializer<'lua> {
impl<'a> Serializer<'a> {
/// Creates a new Lua Serializer with default options.
pub fn new(lua: &'lua Lua) -> Self {
pub fn new(lua: &'a Lua) -> Self {
Self::new_with_options(lua, Options::default())
}
/// Creates a new Lua Serializer with custom options.
pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self {
pub fn new_with_options(lua: &'a Lua, options: Options) -> Self {
Serializer { lua, options }
}
}
@@ -106,28 +127,28 @@ impl<'lua> Serializer<'lua> {
macro_rules! lua_serialize_number {
($name:ident, $t:ty) => {
#[inline]
fn $name(self, value: $t) -> Result<Value<'lua>> {
value.to_lua(self.lua)
fn $name(self, value: $t) -> Result<Value> {
value.into_lua(self.lua)
}
};
}
impl<'lua> ser::Serializer for Serializer<'lua> {
type Ok = Value<'lua>;
impl<'a> ser::Serializer for Serializer<'a> {
type Ok = Value;
type Error = Error;
// Associated types for keeping track of additional state while serializing
// compound data structures like sequences and maps.
type SerializeSeq = SerializeVec<'lua>;
type SerializeTuple = SerializeVec<'lua>;
type SerializeTupleStruct = SerializeVec<'lua>;
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
type SerializeMap = SerializeMap<'lua>;
type SerializeStruct = SerializeMap<'lua>;
type SerializeStructVariant = SerializeStructVariant<'lua>;
type SerializeSeq = SerializeSeq<'a>;
type SerializeTuple = SerializeSeq<'a>;
type SerializeTupleStruct = SerializeSeq<'a>;
type SerializeTupleVariant = SerializeTupleVariant<'a>;
type SerializeMap = SerializeMap<'a>;
type SerializeStruct = SerializeStruct<'a>;
type SerializeStructVariant = SerializeStructVariant<'a>;
#[inline]
fn serialize_bool(self, value: bool) -> Result<Value<'lua>> {
fn serialize_bool(self, value: bool) -> Result<Value> {
Ok(Value::Boolean(value))
}
@@ -139,27 +160,29 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
lua_serialize_number!(serialize_u32, u32);
lua_serialize_number!(serialize_i64, i64);
lua_serialize_number!(serialize_u64, u64);
lua_serialize_number!(serialize_i128, i128);
lua_serialize_number!(serialize_u128, u128);
lua_serialize_number!(serialize_f32, f32);
lua_serialize_number!(serialize_f64, f64);
#[inline]
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
fn serialize_char(self, value: char) -> Result<Value> {
self.serialize_str(&value.to_string())
}
#[inline]
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
fn serialize_str(self, value: &str) -> Result<Value> {
self.lua.create_string(value).map(Value::String)
}
#[inline]
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
fn serialize_bytes(self, value: &[u8]) -> Result<Value> {
self.lua.create_string(value).map(Value::String)
}
#[inline]
fn serialize_none(self) -> Result<Value<'lua>> {
fn serialize_none(self) -> Result<Value> {
if self.options.serialize_none_to_null {
Ok(self.lua.null())
} else {
@@ -168,7 +191,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
fn serialize_some<T>(self, value: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
@@ -176,7 +199,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_unit(self) -> Result<Value<'lua>> {
fn serialize_unit(self) -> Result<Value> {
if self.options.serialize_unit_to_null {
Ok(self.lua.null())
} else {
@@ -185,7 +208,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value> {
if self.options.serialize_unit_to_null {
Ok(self.lua.null())
} else {
@@ -199,12 +222,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Value<'lua>> {
) -> Result<Value> {
self.serialize_str(variant)
}
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
@@ -218,7 +241,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Value<'lua>>
) -> Result<Value>
where
T: Serialize + ?Sized,
{
@@ -231,13 +254,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
let len = len.unwrap_or(0) as c_int;
let table = self.lua.create_table_with_capacity(len, 0)?;
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
if self.options.set_array_metatable {
table.set_metatable(Some(self.lua.array_metatable()));
}
let options = self.options;
Ok(SerializeVec { table, options })
Ok(SerializeSeq::new(self.lua, table, self.options))
}
#[inline]
@@ -246,11 +267,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
#[cfg(feature = "luau")]
if name == "Vector" && len == crate::Vector::SIZE {
return Ok(SerializeSeq::new_vector(self.lua, self.options));
}
_ = name;
self.serialize_seq(Some(len))
}
@@ -263,7 +285,8 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
_len: usize,
) -> Result<Self::SerializeTupleVariant> {
Ok(SerializeTupleVariant {
name: self.lua.create_string(variant)?,
lua: self.lua,
variant,
table: self.lua.create_table()?,
options: self.options,
})
@@ -271,17 +294,32 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
let len = len.unwrap_or(0) as c_int;
Ok(SerializeMap {
lua: self.lua,
key: None,
table: self.lua.create_table_with_capacity(0, len)?,
table: self.lua.create_table_with_capacity(0, len.unwrap_or(0))?,
options: self.options,
})
}
#[inline]
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
self.serialize_map(Some(len))
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
if self.options.detect_serde_json_arbitrary_precision
&& name == "$serde_json::private::Number"
&& len == 1
{
return Ok(SerializeStruct {
lua: self.lua,
inner: None,
options: self.options,
});
}
Ok(SerializeStruct {
lua: self.lua,
inner: Some(Value::Table(self.lua.create_table_with_capacity(0, len)?)),
options: self.options,
})
}
#[inline]
@@ -293,49 +331,70 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
len: usize,
) -> Result<Self::SerializeStructVariant> {
Ok(SerializeStructVariant {
name: self.lua.create_string(variant)?,
table: self.lua.create_table_with_capacity(0, len as c_int)?,
lua: self.lua,
variant,
table: self.lua.create_table_with_capacity(0, len)?,
options: self.options,
})
}
}
#[doc(hidden)]
pub struct SerializeVec<'lua> {
table: Table<'lua>,
pub struct SerializeSeq<'a> {
lua: &'a Lua,
#[cfg(feature = "luau")]
vector: Option<crate::Vector>,
table: Option<Table>,
next: usize,
options: Options,
}
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
type Ok = Value<'lua>;
impl<'a> SerializeSeq<'a> {
fn new(lua: &'a Lua, table: Table, options: Options) -> Self {
Self {
lua,
#[cfg(feature = "luau")]
vector: None,
table: Some(table),
next: 0,
options,
}
}
#[cfg(feature = "luau")]
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
Self {
lua,
vector: Some(crate::Vector::zero()),
table: None,
next: 0,
options,
}
}
}
impl ser::SerializeSeq for SerializeSeq<'_> {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
protect_lua(lua.state, 2, 0, |state| {
ffi::lua_rawseti(state, -2, len + 1);
})
}
let value = self.lua.to_value_with(value, self.options)?;
let table = self.table.as_ref().unwrap();
table.raw_seti(self.next + 1, value)?;
self.next += 1;
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
Ok(Value::Table(self.table))
fn end(self) -> Result<Value> {
Ok(Value::Table(self.table.unwrap()))
}
}
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeTuple for SerializeSeq<'_> {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
@@ -345,73 +404,82 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
ser::SerializeSeq::end(self)
}
}
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeTupleStruct for SerializeSeq<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
#[cfg(feature = "luau")]
if let Some(vector) = self.vector.as_mut() {
let value = self.lua.to_value_with(value, self.options)?;
let value = self.lua.unpack(value)?;
vector.0[self.next] = value;
self.next += 1;
return Ok(());
}
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
#[cfg(feature = "luau")]
if let Some(vector) = self.vector {
return Ok(Value::Vector(vector));
}
ser::SerializeSeq::end(self)
}
}
#[doc(hidden)]
pub struct SerializeTupleVariant<'lua> {
name: String<'lua>,
table: Table<'lua>,
pub struct SerializeTupleVariant<'a> {
lua: &'a Lua,
variant: &'static str,
table: Table,
options: Options,
}
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeTupleVariant for SerializeTupleVariant<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let idx = self.table.raw_len() + 1;
self.table
.raw_insert(idx, lua.to_value_with(value, self.options)?)
self.table.raw_push(self.lua.to_value_with(value, self.options)?)
}
fn end(self) -> Result<Value<'lua>> {
let lua = self.table.0.lua;
let table = lua.create_table()?;
table.raw_set(self.name, self.table)?;
fn end(self) -> Result<Value> {
let table = self.lua.create_table()?;
table.raw_set(self.variant, self.table)?;
Ok(Value::Table(table))
}
}
#[doc(hidden)]
pub struct SerializeMap<'lua> {
table: Table<'lua>,
key: Option<Value<'lua>>,
pub struct SerializeMap<'a> {
lua: &'a Lua,
table: Table,
key: Option<Value>,
options: Options,
}
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeMap for SerializeMap<'_> {
type Ok = Value;
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.key = Some(lua.to_value_with(key, self.options)?);
self.key = Some(self.lua.to_value_with(key, self.options)?);
Ok(())
}
@@ -419,62 +487,90 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let key = mlua_expect!(
self.key.take(),
"serialize_value called before serialize_key"
);
let value = lua.to_value_with(value, self.options)?;
let key = mlua_expect!(self.key.take(), "serialize_value called before serialize_key");
let value = self.lua.to_value_with(value, self.options)?;
self.table.raw_set(key, value)
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
Ok(Value::Table(self.table))
}
}
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
type Ok = Value<'lua>;
#[doc(hidden)]
pub struct SerializeStruct<'a> {
lua: &'a Lua,
inner: Option<Value>,
options: Options,
}
impl ser::SerializeStruct for SerializeStruct<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
ser::SerializeMap::serialize_key(self, key)?;
ser::SerializeMap::serialize_value(self, value)
match self.inner {
Some(Value::Table(ref table)) => {
table.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
}
None if self.options.detect_serde_json_arbitrary_precision => {
// A special case for `serde_json::Number` with arbitrary precision.
assert_eq!(key, "$serde_json::private::Number");
self.inner = Some(self.lua.to_value_with(value, self.options)?);
}
_ => unreachable!(),
}
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
ser::SerializeMap::end(self)
fn end(self) -> Result<Value> {
match self.inner {
Some(table @ Value::Table(_)) => Ok(table),
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
let number_s = value.as_str().expect("not an arbitrary precision number");
if number_s.contains(['.', 'e', 'E']) {
if let Ok(number) = number_s.parse().map(Value::Number) {
return Ok(number);
}
}
Ok(number_s
.parse()
.map(Value::Integer)
.or_else(|_| number_s.parse().map(Value::Number))
.unwrap_or(value))
}
_ => unreachable!(),
}
}
}
#[doc(hidden)]
pub struct SerializeStructVariant<'lua> {
name: String<'lua>,
table: Table<'lua>,
pub struct SerializeStructVariant<'a> {
lua: &'a Lua,
variant: &'static str,
table: Table,
options: Options,
}
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeStructVariant for SerializeStructVariant<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.table
.raw_set(key, lua.to_value_with(value, self.options)?)?;
.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
let lua = self.table.0.lua;
let table = lua.create_table()?;
table.raw_set(self.name, self.table)?;
fn end(self) -> Result<Value> {
let table = self.lua.create_table_with_capacity(0, 1)?;
table.raw_set(self.variant, self.table)?;
Ok(Value::Table(table))
}
}
+2078
View File
File diff suppressed because it is too large Load Diff
+255
View File
@@ -0,0 +1,255 @@
use std::any::TypeId;
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::rc::Rc;
use std::sync::Arc;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use crate::error::Result;
use crate::state::RawLua;
use crate::stdlib::StdLib;
use crate::types::{AppData, ReentrantMutex, XRc};
use crate::userdata::RawUserDataRegistry;
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
#[cfg(any(feature = "luau", doc))]
use crate::chunk::Compiler;
#[cfg(feature = "async")]
use {futures_util::task::noop_waker_ref, std::ptr::NonNull, std::task::Waker};
use super::{Lua, WeakLua};
// Unique key to store `ExtraData` in the registry
static EXTRA_REGISTRY_KEY: u8 = 0;
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
const REF_STACK_RESERVE: c_int = 2;
/// Data associated with the Lua state.
pub(crate) struct ExtraData {
pub(super) lua: MaybeUninit<Lua>,
pub(super) weak: MaybeUninit<WeakLua>,
pub(super) owned: bool,
pub(super) pending_userdata_reg: FxHashMap<TypeId, RawUserDataRegistry>,
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
// Container to store arbitrary data (extensions)
pub(super) app_data: AppData,
pub(super) safe: bool,
pub(super) libs: StdLib,
// Used in module mode
pub(super) skip_memory_check: bool,
// Auxiliary thread to store references
pub(super) ref_thread: *mut ffi::lua_State,
pub(super) ref_stack_size: c_int,
pub(super) ref_stack_top: c_int,
pub(super) ref_free: Vec<c_int>,
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
pub(super) wrapped_failure_pool: Vec<c_int>,
pub(super) wrapped_failure_top: usize,
// Pool of `Thread`s (coroutines) for async execution
#[cfg(feature = "async")]
pub(super) thread_pool: Vec<c_int>,
// Address of `WrappedFailure` metatable
pub(super) wrapped_failure_mt_ptr: *const c_void,
// Waker for polling futures
#[cfg(feature = "async")]
pub(super) waker: NonNull<Waker>,
#[cfg(not(feature = "luau"))]
pub(super) hook_callback: Option<crate::types::HookCallback>,
#[cfg(not(feature = "luau"))]
pub(super) hook_thread: *mut ffi::lua_State,
#[cfg(feature = "lua54")]
pub(super) warn_callback: Option<crate::types::WarnCallback>,
#[cfg(feature = "luau")]
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
#[cfg(feature = "luau")]
pub(crate) running_userdata_gc: bool,
#[cfg(feature = "luau")]
pub(super) sandboxed: bool,
#[cfg(feature = "luau")]
pub(super) compiler: Option<Compiler>,
#[cfg(feature = "luau-jit")]
pub(super) enable_jit: bool,
}
impl Drop for ExtraData {
fn drop(&mut self) {
unsafe {
if !self.owned {
self.lua.assume_init_drop();
}
self.weak.assume_init_drop();
}
*self.registry_unref_list.lock() = None;
}
}
static EXTRA_TYPE_KEY: u8 = 0;
impl TypeKey for XRc<UnsafeCell<ExtraData>> {
#[inline(always)]
fn type_key() -> *const c_void {
&EXTRA_TYPE_KEY as *const u8 as *const c_void
}
}
impl ExtraData {
// Index of `error_traceback` function in auxiliary thread stack
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
pub(super) const ERROR_TRACEBACK_IDX: c_int = 1;
pub(super) unsafe fn init(state: *mut ffi::lua_State, owned: bool) -> XRc<UnsafeCell<Self>> {
// Create ref stack thread and place it in the registry to prevent it
// from being garbage collected.
let ref_thread = mlua_expect!(
protect_lua!(state, 0, 0, |state| {
let thread = ffi::lua_newthread(state);
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
thread
}),
"Error while creating ref thread",
);
let wrapped_failure_mt_ptr = {
get_internal_metatable::<WrappedFailure>(state);
let ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ptr
};
// Store `error_traceback` function on the ref stack
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
{
ffi::lua_pushcfunction(ref_thread, crate::util::error_traceback);
assert_eq!(ffi::lua_gettop(ref_thread), Self::ERROR_TRACEBACK_IDX);
}
#[allow(clippy::arc_with_non_send_sync)]
let extra = XRc::new(UnsafeCell::new(ExtraData {
lua: MaybeUninit::uninit(),
weak: MaybeUninit::uninit(),
owned,
pending_userdata_reg: FxHashMap::default(),
registered_userdata_t: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
app_data: AppData::default(),
safe: false,
libs: StdLib::NONE,
skip_memory_check: false,
ref_thread,
// We need some reserved stack space to move values in and out of the ref stack.
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
ref_stack_top: ffi::lua_gettop(ref_thread),
ref_free: Vec::new(),
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY),
wrapped_failure_top: 0,
#[cfg(feature = "async")]
thread_pool: Vec::new(),
wrapped_failure_mt_ptr,
#[cfg(feature = "async")]
waker: NonNull::from(noop_waker_ref()),
#[cfg(not(feature = "luau"))]
hook_callback: None,
#[cfg(not(feature = "luau"))]
hook_thread: ptr::null_mut(),
#[cfg(feature = "lua54")]
warn_callback: None,
#[cfg(feature = "luau")]
interrupt_callback: None,
#[cfg(feature = "luau")]
sandboxed: false,
#[cfg(feature = "luau")]
compiler: None,
#[cfg(feature = "luau-jit")]
enable_jit: true,
#[cfg(feature = "luau")]
running_userdata_gc: false,
}));
// Store it in the registry
mlua_expect!(Self::store(&extra, state), "Error while storing extra data");
extra
}
pub(super) unsafe fn set_lua(&mut self, raw: &XRc<ReentrantMutex<RawLua>>) {
self.lua.write(Lua {
raw: XRc::clone(raw),
collect_garbage: false,
});
if self.owned {
XRc::decrement_strong_count(XRc::as_ptr(raw));
}
self.weak.write(WeakLua(XRc::downgrade(raw)));
}
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
#[cfg(feature = "luau")]
if cfg!(not(feature = "module")) {
// In the main app we can use `lua_callbacks` to access ExtraData
return (*ffi::lua_callbacks(state)).userdata as *mut _;
}
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
// `ExtraData` can be null only when Lua state is foreign.
// This case in used in `Lua::try_from_ptr()`.
ffi::lua_pop(state, 1);
return ptr::null_mut();
}
let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Rc<UnsafeCell<ExtraData>>;
ffi::lua_pop(state, 1);
(*extra_ptr).get()
}
unsafe fn store(extra: &XRc<UnsafeCell<Self>>, state: *mut ffi::lua_State) -> Result<()> {
#[cfg(feature = "luau")]
if cfg!(not(feature = "module")) {
(*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _;
return Ok(());
}
push_internal_userdata(state, XRc::clone(extra), true)?;
protect_lua!(state, 1, 0, fn(state) {
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
})
}
#[inline(always)]
pub(super) unsafe fn lua(&self) -> &Lua {
self.lua.assume_init_ref()
}
#[inline(always)]
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
&*self.lua.assume_init_ref().raw.data_ptr()
}
#[inline(always)]
pub(super) unsafe fn weak(&self) -> &WeakLua {
self.weak.assume_init_ref()
}
}
+1406
View File
File diff suppressed because it is too large Load Diff
+172
View File
@@ -0,0 +1,172 @@
use std::os::raw::c_int;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::ptr;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::state::{ExtraData, RawLua};
use crate::util::{self, get_internal_metatable, WrappedFailure};
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
impl<'a> StateGuard<'a> {
fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
state = inner.state.replace(state);
Self(inner, state)
}
}
impl Drop for StateGuard<'_> {
fn drop(&mut self) {
self.0.state.set(self.1);
}
}
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
// and instead reuses unused values from previous calls (or allocates new).
pub(super) unsafe fn callback_error_ext<F, R>(
state: *mut ffi::lua_State,
mut extra: *mut ExtraData,
f: F,
) -> R
where
F: FnOnce(*mut ExtraData, c_int) -> Result<R>,
{
if extra.is_null() {
extra = ExtraData::get(state);
}
let nargs = ffi::lua_gettop(state);
enum PreallocatedFailure {
New(*mut WrappedFailure),
Reserved,
}
impl PreallocatedFailure {
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
if (*extra).wrapped_failure_top > 0 {
(*extra).wrapped_failure_top -= 1;
return PreallocatedFailure::Reserved;
}
// We need to check stack for Luau in case when callback is called from interrupt
// See https://github.com/luau-lang/luau/issues/446 and mlua #142 and #153
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
// Place it to the beginning of the stack
let ud = WrappedFailure::new_userdata(state);
ffi::lua_insert(state, 1);
PreallocatedFailure::New(ud)
}
#[cold]
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
let ref_thread = (*extra).ref_thread;
match *self {
PreallocatedFailure::New(ud) => {
ffi::lua_settop(state, 1);
ud
}
PreallocatedFailure::Reserved => {
let index = (*extra).wrapped_failure_pool.pop().unwrap();
ffi::lua_settop(state, 0);
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
ffi::lua_xpush(ref_thread, state, index);
ffi::lua_pushnil(ref_thread);
ffi::lua_replace(ref_thread, index);
(*extra).ref_free.push(index);
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
}
}
}
unsafe fn release(self, state: *mut ffi::lua_State, extra: *mut ExtraData) {
let ref_thread = (*extra).ref_thread;
match self {
PreallocatedFailure::New(_) => {
ffi::lua_rotate(state, 1, -1);
ffi::lua_xmove(state, ref_thread, 1);
let index = ref_stack_pop(extra);
(*extra).wrapped_failure_pool.push(index);
(*extra).wrapped_failure_top += 1;
}
PreallocatedFailure::Reserved => (*extra).wrapped_failure_top += 1,
}
}
}
// We cannot shadow Rust errors with Lua ones, so we need to reserve pre-allocated memory
// to store a wrapped failure (error or panic) *before* we proceed.
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
match catch_unwind(AssertUnwindSafe(|| {
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
f(extra, nargs)
})) {
Ok(Ok(r)) => {
// Return unused `WrappedFailure` to the pool
prealloc_failure.release(state, extra);
r
}
Ok(Err(err)) => {
let wrapped_error = prealloc_failure.r#use(state, extra);
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
let traceback = util::to_string(state, -1);
ffi::lua_pop(state, 1);
traceback
} else {
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
ptr::write(
wrapped_error,
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
);
get_internal_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
Err(p) => {
let wrapped_panic = prealloc_failure.r#use(state, extra);
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
get_internal_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
}
}
pub(super) unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
let extra = &mut *extra;
if let Some(free) = extra.ref_free.pop() {
ffi::lua_replace(extra.ref_thread, free);
return free;
}
// Try to grow max stack size
if extra.ref_stack_top >= extra.ref_stack_size {
let mut inc = extra.ref_stack_size; // Try to double stack size
while inc > 0 && ffi::lua_checkstack(extra.ref_thread, inc) == 0 {
inc /= 2;
}
if inc == 0 {
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
// during unwinding.
ffi::lua_pop(extra.ref_thread, 1);
let top = extra.ref_stack_top;
// It is a user error to create enough references to exhaust the Lua max stack size for
// the ref thread.
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
}
extra.ref_stack_size += inc;
}
extra.ref_stack_top += 1;
extra.ref_stack_top
}
+43 -17
View File
@@ -1,50 +1,73 @@
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
use std::u32;
/// Flags describing the set of lua standard libraries to load.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct StdLib(u32);
impl StdLib {
/// [`coroutine`](https://www.lua.org/manual/5.3/manual.html#6.2) library
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
///
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub const COROUTINE: StdLib = StdLib(1);
/// [`table`](https://www.lua.org/manual/5.3/manual.html#6.6) library
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
pub const TABLE: StdLib = StdLib(1 << 1);
/// [`io`](https://www.lua.org/manual/5.3/manual.html#6.8) library
/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub const IO: StdLib = StdLib(1 << 2);
/// [`os`](https://www.lua.org/manual/5.3/manual.html#6.9) library
/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
pub const OS: StdLib = StdLib(1 << 3);
/// [`string`](https://www.lua.org/manual/5.3/manual.html#6.4) library
/// [`string`](https://www.lua.org/manual/5.4/manual.html#6.4) library
pub const STRING: StdLib = StdLib(1 << 4);
/// [`utf8`](https://www.lua.org/manual/5.3/manual.html#6.5) library
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
///
/// Requires `feature = "lua54/lua53"`
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
/// Requires `feature = "lua54/lua53/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
pub const UTF8: StdLib = StdLib(1 << 5);
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
///
/// Requires `feature = "lua52/luajit"`
#[cfg(any(feature = "lua52", feature = "luajit", doc))]
/// Requires `feature = "lua52/luajit/luau"`
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
pub const BIT: StdLib = StdLib(1 << 6);
/// [`math`](https://www.lua.org/manual/5.3/manual.html#6.7) library
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
pub const MATH: StdLib = StdLib(1 << 7);
/// [`package`](https://www.lua.org/manual/5.3/manual.html#6.3) library
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
pub const PACKAGE: StdLib = StdLib(1 << 8);
/// [`buffer`](https://luau.org/library#buffer-library) library
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub const BUFFER: StdLib = StdLib(1 << 9);
/// [`vector`](https://luau.org/library#vector-library) library
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub const VECTOR: StdLib = StdLib(1 << 10);
/// [`jit`](http://luajit.org/ext_jit.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
pub const JIT: StdLib = StdLib(1 << 9);
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const JIT: StdLib = StdLib(1 << 11);
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const FFI: StdLib = StdLib(1 << 30);
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
pub const DEBUG: StdLib = StdLib(1 << 31);
/// No libraries
@@ -52,7 +75,10 @@ impl StdLib {
/// (**unsafe**) All standard libraries
pub const ALL: StdLib = StdLib(u32::MAX);
/// The safe subset of the standard libraries
#[cfg(not(feature = "luau"))]
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);
#[cfg(feature = "luau")]
pub const ALL_SAFE: StdLib = StdLib(u32::MAX);
pub fn contains(self, lib: Self) -> bool {
(self & lib).0 != 0
+331 -47
View File
@@ -1,6 +1,15 @@
use std::borrow::Cow;
use std::borrow::{Borrow, Cow};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
use std::{slice, str};
use std::{cmp, fmt, slice, str};
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::traits::IntoLua;
use crate::types::{LuaType, ValueRef};
use crate::value::Value;
#[cfg(feature = "serialize")]
use {
@@ -8,19 +17,14 @@ use {
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, StackGuard};
/// Handle to an internal Lua string.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
#[derive(Clone, Debug)]
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
#[derive(Clone)]
pub struct String(pub(crate) ValueRef);
impl<'lua> String<'lua> {
/// Get a `&str` slice if the Lua string is valid UTF-8.
impl String {
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
///
/// # Examples
///
@@ -38,19 +42,20 @@ impl<'lua> String<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn to_str(&self) -> Result<&str> {
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str",
message: Some(e.to_string()),
})
#[inline]
pub fn to_str(&self) -> Result<BorrowedStr> {
BorrowedStr::try_from(self)
}
/// Converts this string to a [`Cow<str>`].
/// Converts this string to a [`StdString`].
///
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
/// bound to a weak Lua object.
///
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
/// [`Cow<'_, str>`]: std::borrow::Cow
///
/// # Examples
///
@@ -64,8 +69,19 @@ impl<'lua> String<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn to_string_lossy(&self) -> Cow<'_, str> {
StdString::from_utf8_lossy(self.as_bytes())
#[inline]
pub fn to_string_lossy(&self) -> StdString {
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
}
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
/// contain non-Unicode data.
///
/// This may perform lossy conversion.
///
/// [`Display`]: fmt::Display
pub fn display(&self) -> impl fmt::Display + '_ {
Display(self)
}
/// Get the bytes that make up this string.
@@ -85,66 +101,334 @@ impl<'lua> String<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn as_bytes(&self) -> &[u8] {
let nulled = self.as_bytes_with_nul();
&nulled[..nulled.len() - 1]
#[inline]
pub fn as_bytes(&self) -> BorrowedBytes {
BorrowedBytes::from(self)
}
/// Get the bytes that make up this string, including the trailing nul byte.
pub fn as_bytes_with_nul(&self) -> &[u8] {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
// Include the trailing nul byte (it's always present but excluded by default)
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
BorrowedBytes { buf, borrow, _lua }
}
// Does not return the terminating nul byte
unsafe fn to_slice(&self) -> (&[u8], Lua) {
let lua = self.0.lua.upgrade();
let slice = {
let rawlua = lua.lock();
let ref_thread = rawlua.ref_thread();
lua.push_ref(&self.0);
mlua_debug_assert!(
ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING,
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
"string ref is not string type"
);
let mut size = 0;
// This will not trigger a 'm' error, because the reference is guaranteed to be of
// string type
let data = ffi::lua_tolstring(lua.state, -1, &mut size);
let mut size = 0;
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
slice::from_raw_parts(data as *const u8, size)
};
(slice, lua)
}
slice::from_raw_parts(data as *const u8, size + 1)
/// Converts this string to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
}
impl fmt::Debug for String {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.as_bytes();
// Check if the string is valid utf8
if let Ok(s) = str::from_utf8(&bytes) {
return s.fmt(f);
}
// Format as bytes
write!(f, "b")?;
<bstr::BStr as fmt::Debug>::fmt(bstr::BStr::new(&bytes), f)
}
}
impl<'lua> AsRef<[u8]> for String<'lua> {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
//
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str`, `String` and `mlua::String`
// itself.
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
//
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
// in other ways.
impl<'lua, T> PartialEq<T> for String<'lua>
impl<T> PartialEq<T> for String
where
T: AsRef<[u8]>,
T: AsRef<[u8]> + ?Sized,
{
fn eq(&self, other: &T) -> bool {
self.as_bytes() == other.as_ref()
}
}
impl PartialEq for String {
fn eq(&self, other: &String) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl Eq for String {}
impl<T> PartialOrd<T> for String
where
T: AsRef<[u8]> + ?Sized,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(&other.as_ref())
}
}
impl PartialOrd for String {
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for String {
fn cmp(&self, other: &String) -> cmp::Ordering {
self.as_bytes().cmp(&other.as_bytes())
}
}
impl Hash for String {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for String<'lua> {
impl Serialize for String {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
match self.to_str() {
Ok(s) => serializer.serialize_str(s),
Err(_) => serializer.serialize_bytes(self.as_bytes()),
Ok(s) => serializer.serialize_str(&s),
Err(_) => serializer.serialize_bytes(&self.as_bytes()),
}
}
}
struct Display<'a>(&'a String);
impl fmt::Display for Display<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.0.as_bytes();
<bstr::BStr as fmt::Display>::fmt(bstr::BStr::new(&bytes), f)
}
}
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
pub struct BorrowedStr<'a> {
// `buf` points to a readonly memory managed by Lua
pub(crate) buf: &'a str,
pub(crate) borrow: Cow<'a, String>,
pub(crate) _lua: Lua,
}
impl Deref for BorrowedStr<'_> {
type Target = str;
#[inline(always)]
fn deref(&self) -> &str {
self.buf
}
}
impl Borrow<str> for BorrowedStr<'_> {
#[inline(always)]
fn borrow(&self) -> &str {
self.buf
}
}
impl AsRef<str> for BorrowedStr<'_> {
#[inline(always)]
fn as_ref(&self) -> &str {
self.buf
}
}
impl fmt::Display for BorrowedStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.buf.fmt(f)
}
}
impl fmt::Debug for BorrowedStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.buf.fmt(f)
}
}
impl<T> PartialEq<T> for BorrowedStr<'_>
where
T: AsRef<str>,
{
fn eq(&self, other: &T) -> bool {
self.buf == other.as_ref()
}
}
impl Eq for BorrowedStr<'_> {}
impl<T> PartialOrd<T> for BorrowedStr<'_>
where
T: AsRef<str>,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.buf.partial_cmp(other.as_ref())
}
}
impl Ord for BorrowedStr<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.buf.cmp(other.buf)
}
}
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
type Error = Error;
#[inline]
fn try_from(value: &'a String) -> Result<Self> {
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str".to_string(),
message: Some(e.to_string()),
})?;
Ok(Self { buf, borrow, _lua })
}
}
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
pub struct BorrowedBytes<'a> {
// `buf` points to a readonly memory managed by Lua
pub(crate) buf: &'a [u8],
pub(crate) borrow: Cow<'a, String>,
pub(crate) _lua: Lua,
}
impl Deref for BorrowedBytes<'_> {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &[u8] {
self.buf
}
}
impl Borrow<[u8]> for BorrowedBytes<'_> {
#[inline(always)]
fn borrow(&self) -> &[u8] {
self.buf
}
}
impl AsRef<[u8]> for BorrowedBytes<'_> {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.buf
}
}
impl fmt::Debug for BorrowedBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.buf.fmt(f)
}
}
impl<T> PartialEq<T> for BorrowedBytes<'_>
where
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
self.buf == other.as_ref()
}
}
impl Eq for BorrowedBytes<'_> {}
impl<T> PartialOrd<T> for BorrowedBytes<'_>
where
T: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.buf.partial_cmp(other.as_ref())
}
}
impl Ord for BorrowedBytes<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.buf.cmp(other.buf)
}
}
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
type Item = &'a u8;
type IntoIter = slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> From<&'a String> for BorrowedBytes<'a> {
#[inline]
fn from(value: &'a String) -> Self {
let (buf, _lua) = unsafe { value.to_slice() };
let borrow = Cow::Borrowed(value);
Self { buf, borrow, _lua }
}
}
struct WrappedString<T: AsRef<[u8]>>(T);
impl String {
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
///
/// This function uses [`Lua::create_string`] under the hood.
pub fn wrap(data: impl AsRef<[u8]>) -> impl IntoLua {
WrappedString(data)
}
}
impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
fn into_lua(self, lua: &Lua) -> Result<Value> {
lua.create_string(self.0).map(Value::String)
}
}
impl LuaType for String {
const TYPE_ID: c_int = ffi::LUA_TSTRING;
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(String: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(String: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(BorrowedStr: Send, Sync);
}
+798 -362
View File
File diff suppressed because it is too large Load Diff
+393 -212
View File
@@ -1,81 +1,110 @@
use std::cmp;
use std::os::raw::c_int;
use std::fmt;
use std::os::raw::{c_int, c_void};
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
use crate::function::Function;
use crate::state::RawLua;
use crate::traits::{FromLuaMulti, IntoLuaMulti};
use crate::types::{LuaType, ValueRef};
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
#[cfg(not(feature = "luau"))]
use crate::{
hook::{Debug, HookTriggers},
types::MaybeSend,
};
#[cfg(feature = "async")]
use {
crate::{
lua::{ASYNC_POLL_PENDING, WAKER_REGISTRY_KEY},
util::get_gc_userdata,
value::Value,
},
futures_core::{future::Future, stream::Stream},
futures_util::stream::Stream,
std::{
cell::RefCell,
future::Future,
marker::PhantomData,
mem,
os::raw::c_void,
pin::Pin,
ptr::NonNull,
task::{Context, Poll, Waker},
},
};
/// Status of a Lua thread (or coroutine).
/// Status of a Lua thread (coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadStatus {
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
/// The thread was just created or is suspended (yielded).
///
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
///
/// [`Thread::resume`]: struct.Thread.html#method.resume
Resumable,
/// Either the thread has finished executing, or the thread is currently running.
Unresumable,
/// The thread is currently running.
Running,
/// The thread has finished executing.
Finished,
/// The thread has raised a Lua error during execution.
Error,
}
/// Handle to an internal Lua thread (or coroutine).
#[derive(Clone, Debug)]
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// Internal representation of a Lua thread status.
///
/// The number in `New` and `Yielded` variants is the number of arguments pushed
/// to the thread stack.
#[derive(Clone, Copy)]
enum ThreadStatusInner {
New,
Running,
Yielded,
Finished,
Error,
}
impl ThreadStatusInner {
#[inline(always)]
fn is_resumable(self) -> bool {
matches!(self, ThreadStatusInner::New | ThreadStatusInner::Yielded)
}
}
/// Handle to an internal Lua thread (coroutine).
#[derive(Clone)]
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
#[cfg(feature = "send")]
unsafe impl Send for Thread {}
#[cfg(feature = "send")]
unsafe impl Sync for Thread {}
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
///
/// Requires `feature = "async"`
///
/// [`Future`]: ../futures_core/future/trait.Future.html
/// [`Stream`]: ../futures_core/stream/trait.Stream.html
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[derive(Debug)]
pub struct AsyncThread<'lua, R> {
thread: Thread<'lua>,
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AsyncThread<A, R> {
thread: Thread,
init_args: Option<A>,
ret: PhantomData<R>,
recycle: bool,
}
impl<'lua> Thread<'lua> {
impl Thread {
#[inline(always)]
fn state(&self) -> *mut ffi::lua_State {
self.1
}
/// Resumes execution of this thread.
///
/// Equivalent to `coroutine.resume`.
/// Equivalent to [`coroutine.resume`].
///
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
/// are passed to its main function.
/// Passes `args` as arguments to the thread. If the coroutine has called [`coroutine.yield`],
/// it will return these arguments. Otherwise, the coroutine wasn't yet started, so the
/// arguments are passed to its main function.
///
/// If the thread is no longer in `Active` state (meaning it has finished execution or
/// encountered an error), this will return `Err(CoroutineInactive)`, otherwise will return `Ok`
/// as follows:
/// If the thread is no longer resumable (meaning it has finished execution or encountered an
/// error), this will return [`Error::CoroutineUnresumable`], otherwise will return `Ok` as
/// follows:
///
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
/// If the thread calls [`coroutine.yield`], returns the values passed to `yield`. If the thread
/// `return`s values from its main function, returns those.
///
/// # Examples
@@ -93,83 +122,110 @@ impl<'lua> Thread<'lua> {
/// end)
/// "#).eval()?;
///
/// assert_eq!(thread.resume::<_, u32>(42)?, 123);
/// assert_eq!(thread.resume::<_, u32>(43)?, 987);
/// assert_eq!(thread.resume::<u32>(42)?, 123);
/// assert_eq!(thread.resume::<u32>(43)?, 987);
///
/// // The coroutine has now returned, so `resume` will fail
/// match thread.resume::<_, u32>(()) {
/// Err(Error::CoroutineInactive) => {},
/// match thread.resume::<u32>(()) {
/// Err(Error::CoroutineUnresumable) => {},
/// unexpected => panic!("unexpected result {:?}", unexpected),
/// }
/// # Ok(())
/// # }
/// ```
pub fn resume<A, R>(&self, args: A) -> Result<R>
///
/// [`coroutine.resume`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.resume
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
R: FromLuaMulti,
{
let lua = self.0.lua;
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, cmp::min(nargs + 1, 3))?;
let lua = self.0.lua.lock();
if !self.status_inner(&lua).is_resumable() {
return Err(Error::CoroutineUnresumable);
}
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1);
let state = lua.state();
let thread_state = self.state();
unsafe {
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
return Err(Error::CoroutineInactive);
}
let nresults = self.resume_inner(&lua, args)?;
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
R::from_stack_multi(nresults, &lua)
}
}
/// Resumes execution of this thread.
///
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
unsafe fn resume_inner(&self, lua: &RawLua, args: impl IntoLuaMulti) -> Result<c_int> {
let state = lua.state();
let thread_state = self.state();
let nargs = args.push_into_stack_multi(lua)?;
if nargs > 0 {
check_stack(thread_state, nargs)?;
for arg in args {
lua.push_value(arg)?;
}
ffi::lua_xmove(lua.state, thread_state, nargs);
ffi::lua_xmove(state, thread_state, nargs);
}
let mut nresults = 0;
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
protect_lua(lua.state, 0, 0, |_| error_traceback(thread_state))?;
let mut nresults = 0;
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
if ret == ffi::LUA_ERRMEM {
// Don't call error handler for memory errors
return Err(pop_error(thread_state, ret));
}
check_stack(state, 3)?;
protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
return Err(pop_error(state, ret));
}
let mut results = MultiValue::new();
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
ffi::lua_xmove(thread_state, lua.state, nresults);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
results
};
R::from_lua_multi(results, lua)
Ok(nresults)
}
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
let lua = self.0.lua;
match self.status_inner(&self.0.lua.lock()) {
ThreadStatusInner::New | ThreadStatusInner::Yielded => ThreadStatus::Resumable,
ThreadStatusInner::Running => ThreadStatus::Running,
ThreadStatusInner::Finished => ThreadStatus::Finished,
ThreadStatusInner::Error => ThreadStatus::Error,
}
}
/// Gets the status of the thread (internal implementation).
fn status_inner(&self, lua: &RawLua) -> ThreadStatusInner {
let thread_state = self.state();
if thread_state == lua.state() {
// The thread is currently running
return ThreadStatusInner::Running;
}
let status = unsafe { ffi::lua_status(thread_state) };
match status {
ffi::LUA_YIELD => ThreadStatusInner::Yielded,
ffi::LUA_OK if unsafe { ffi::lua_gettop(thread_state) } > 0 => ThreadStatusInner::New,
ffi::LUA_OK => ThreadStatusInner::Finished,
_ => ThreadStatusInner::Error,
}
}
/// Sets a hook function that will periodically be called as Lua code executes.
///
/// This function is similar or [`Lua::set_hook`] except that it sets for the thread.
/// To remove a hook call [`Lua::remove_hook`].
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
F: Fn(&crate::Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
{
let lua = self.0.lua.lock();
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
ThreadStatus::Error
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
ThreadStatus::Resumable
} else {
ThreadStatus::Unresumable
}
lua.set_thread_hook(self.state(), triggers, callback);
}
}
@@ -179,54 +235,76 @@ impl<'lua> Thread<'lua> {
/// Returns a error in case of either the original error that stopped the thread or errors
/// in closing methods.
///
/// In [LuaJIT]: resets to the initial state of a newly created Lua thread.
/// In Luau: resets to the initial state of a newly created Lua thread.
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
///
/// Other Lua versions can reset only new or finished threads.
///
/// Sets a Lua function for the thread afterwards.
///
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"`
///
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
let lua = self.0.lua;
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
pub fn reset(&self, func: Function) -> Result<()> {
let lua = self.0.lua.lock();
let thread_state = self.state();
match self.status_inner(&lua) {
ThreadStatusInner::Running => return Err(Error::runtime("cannot reset a running thread")),
// Any Lua can reuse new or finished thread
ThreadStatusInner::New => unsafe { ffi::lua_settop(thread_state, 0) },
ThreadStatusInner::Finished => {}
#[cfg(not(any(feature = "lua54", feature = "luau")))]
_ => return Err(Error::runtime("cannot reset non-finished thread")),
#[cfg(any(feature = "lua54", feature = "luau"))]
_ => unsafe {
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
let status = ffi::lua_resetthread(thread_state);
#[cfg(all(feature = "lua54", feature = "vendored"))]
let status = ffi::lua_closethread(thread_state, lua.state());
#[cfg(feature = "lua54")]
if status != ffi::LUA_OK {
return Err(pop_error(thread_state, status));
}
#[cfg(feature = "luau")]
ffi::lua_resetthread(thread_state);
},
}
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
// Push function to the top of the thread stack
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
let ret = ffi::lua_resetthread(lua.state, thread_state);
if ret != ffi::LUA_OK {
return Err(pop_error(thread_state, ret));
#[cfg(feature = "luau")]
{
// Inherit `LUA_GLOBALSINDEX` from the main thread
ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
}
lua.push_ref(&func.0);
ffi::lua_xmove(lua.state, thread_state, 1);
Ok(())
}
}
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
///
/// `args` are passed as arguments to the thread function for first call.
/// The object call `resume()` while polling and also allows to run rust futures
/// The object calls [`resume`] while polling and also allow to run Rust futures
/// to completion using an executor.
///
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
/// values whereas Future version discards that values and poll until the final
/// Using [`AsyncThread`] as a [`Stream`] allow to iterate through [`coroutine.yield`]
/// values whereas [`Future`] version discards that values and poll until the final
/// one (returned from the thread function).
///
/// Requires `feature = "async"`
///
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
/// [`resume`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
///
/// # Examples
///
/// ```
/// # use mlua::{Lua, Result, Thread};
/// use futures::stream::TryStreamExt;
/// use futures_util::stream::TryStreamExt;
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
@@ -240,7 +318,7 @@ impl<'lua> Thread<'lua> {
/// end)
/// "#).eval()?;
///
/// let mut stream = thread.into_async::<_, i64>(1);
/// let mut stream = thread.into_async::<i64>(1);
/// let mut sum = 0;
/// while let Some(n) = stream.try_next().await? {
/// sum += n;
@@ -253,135 +331,238 @@ impl<'lua> Thread<'lua> {
/// ```
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> AsyncThread<impl IntoLuaMulti, R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
R: FromLuaMulti,
{
let args = args.to_lua_multi(self.0.lua);
AsyncThread {
thread: self,
args0: RefCell::new(Some(args)),
init_args: Some(args),
ret: PhantomData,
recycle: false,
}
}
/// Enables sandbox mode on this thread.
///
/// Under the hood replaces the global environment table with a new table,
/// that performs writes locally and proxies reads to caller's global environment.
///
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox`].
///
/// Please note that Luau links environment table with chunk when loading it into Lua state.
/// Therefore you need to load chunks into a thread to link with the thread environment.
///
/// # Examples
///
/// ```
/// # use mlua::{Lua, Result};
/// # #[cfg(feature = "luau")]
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
/// lua2.load("var = 123").exec()?;
/// assert_eq!(lua2.globals().get::<u32>("var")?, 123);
/// Ok(())
/// })?)?;
/// thread.sandbox()?;
/// thread.resume::<()>(())?;
///
/// // The global environment should be unchanged
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
/// # Ok(())
/// # }
///
/// # #[cfg(not(feature = "luau"))]
/// # fn main() { }
/// ```
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
let thread_state = self.state();
unsafe {
check_stack(thread_state, 3)?;
check_stack(state, 3)?;
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
}
}
/// Converts this thread to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
}
impl<'lua> PartialEq for Thread<'lua> {
impl fmt::Debug for Thread {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Thread").field(&self.0).finish()
}
}
impl PartialEq for Thread {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl LuaType for Thread {
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
}
#[cfg(feature = "async")]
impl<'lua, R> Stream for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
impl<A, R> AsyncThread<A, R> {
#[inline(always)]
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
self.recycle = recyclable;
}
}
#[cfg(feature = "async")]
impl<A, R> Drop for AsyncThread<A, R> {
fn drop(&mut self) {
if self.recycle {
if let Some(lua) = self.thread.0.lua.try_lock() {
// For Lua 5.4 this also closes all pending to-be-closed variables
unsafe { lua.recycle_thread(&mut self.thread) };
}
}
}
}
#[cfg(feature = "async")]
impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
type Item = Result<R>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let lua = self.thread.0.lua;
match self.thread.status() {
ThreadStatus::Resumable => {}
_ => return Poll::Ready(None),
};
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
self.thread.resume(())?
};
if is_poll_pending(&ret) {
return Poll::Pending;
let lua = self.thread.0.lua.lock();
if !self.thread.status_inner(&lua).is_resumable() {
return Poll::Ready(None);
}
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(&lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
let nresults = if let Some(args) = this.init_args.take() {
this.thread.resume_inner(&lua, args)?
} else {
this.thread.resume_inner(&lua, ())?
};
if nresults == 1 && is_poll_pending(thread_state) {
return Poll::Pending;
}
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_stack_multi(nresults, &lua)))
}
}
}
#[cfg(feature = "async")]
impl<'lua, R> Future for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let lua = self.thread.0.lua;
match self.thread.status() {
ThreadStatus::Resumable => {}
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
};
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
self.thread.resume(())?
};
if is_poll_pending(&ret) {
return Poll::Pending;
let lua = self.thread.0.lua.lock();
if !self.thread.status_inner(&lua).is_resumable() {
return Poll::Ready(Err(Error::CoroutineUnresumable));
}
if let ThreadStatus::Resumable = self.thread.status() {
// Ignore value returned via yield()
cx.waker().wake_by_ref();
return Poll::Pending;
}
Poll::Ready(R::from_lua_multi(ret, lua))
}
}
#[cfg(feature = "async")]
fn is_poll_pending(val: &MultiValue) -> bool {
match val.iter().enumerate().last() {
Some((1, Value::LightUserData(ud))) => {
ud.0 == &ASYNC_POLL_PENDING as *const u8 as *mut c_void
}
_ => false,
}
}
#[cfg(feature = "async")]
struct WakerGuard(*mut ffi::lua_State, Option<Waker>);
#[cfg(feature = "async")]
impl WakerGuard {
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(&lua, cx.waker());
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
let old = mlua_expect!(waker_slot, "Waker is destroyed").replace(waker);
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
let nresults = if let Some(args) = this.init_args.take() {
this.thread.resume_inner(&lua, args)?
} else {
this.thread.resume_inner(&lua, ())?
};
Ok(WakerGuard(state, old))
if nresults == 1 && is_poll_pending(thread_state) {
return Poll::Pending;
}
if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
// Ignore value returned via yield()
cx.waker().wake_by_ref();
return Poll::Pending;
}
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
Poll::Ready(R::from_stack_multi(nresults, &lua))
}
}
}
#[cfg(feature = "async")]
impl Drop for WakerGuard {
#[inline(always)]
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
ffi::lua_tolightuserdata(state, -1) == crate::Lua::poll_pending().0
}
#[cfg(feature = "async")]
struct WakerGuard<'lua, 'a> {
lua: &'lua RawLua,
prev: NonNull<Waker>,
_phantom: PhantomData<&'a ()>,
}
#[cfg(feature = "async")]
impl<'lua, 'a> WakerGuard<'lua, 'a> {
#[inline]
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
Ok(WakerGuard {
lua,
prev,
_phantom: PhantomData,
})
}
}
#[cfg(feature = "async")]
impl Drop for WakerGuard<'_, '_> {
fn drop(&mut self) {
let state = self.0;
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 3);
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
mem::swap(mlua_expect!(waker_slot, "Waker is destroyed"), &mut self.1);
}
unsafe { self.lua.set_waker(self.prev) };
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Thread: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Thread: Send, Sync);
#[cfg(all(feature = "async", not(feature = "send")))]
static_assertions::assert_not_impl_any!(AsyncThread<(), ()>: Send);
#[cfg(all(feature = "async", feature = "send"))]
static_assertions::assert_impl_all!(AsyncThread<(), ()>: Send, Sync);
}

Some files were not shown because too many files have changed in this diff Show More