Compare commits

...

407 Commits

Author SHA1 Message Date
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
Alex Orlenko 7feabffc37 v0.6.0 2021-06-21 23:19:56 +01:00
Alex Orlenko f24fdfdb80 Update AsChunk::env function prototype 2021-06-21 23:19:33 +01:00
Alex Orlenko c702c5eff2 Add userdata example 2021-06-21 20:28:14 +01:00
Alex Orlenko e4daff8c16 Add limited recursion test to increase coverage 2021-06-21 12:30:17 +01:00
Alex Orlenko 8d474bbf8d Fix clippy warning 2021-06-21 03:25:21 +01:00
Alex Orlenko 9e3b0ecc1e Even better optimization after 14d5c2c887 2021-06-20 19:48:33 +01:00
Alex Orlenko 42b396d0d1 Optimize non-wrapped userdata method calls 2021-06-20 12:38:47 +01:00
Alex Orlenko 14d5c2c887 Lua->Rust callback performance improvements 2021-06-20 00:24:53 +01:00
Alex Orlenko b84c10fde6 Update README (include publishing to luarocks section) 2021-06-19 15:00:04 +01:00
Alex Orlenko b49f9539b6 Update CHANGELOG 2021-06-19 14:59:32 +01:00
Alex Orlenko a208156ed2 Update examples to include chunk! macro 2021-06-19 14:41:48 +01:00
Alex Orlenko 242bdafa75 More tests for arc/rc wrapped userdata 2021-06-19 00:03:09 +01:00
Alex Orlenko b84d1bd65f Update test names 2021-06-18 23:13:56 +01:00
Alex Orlenko 4e92ea341b Fix userdata memleak in edge case.
This can happen if we unable to push metatable with `__gc` metamethod after pushing userdata.
In this case Lua will never execute drop.
Instead, we will push metatable first and then userdata.
2021-06-18 17:45:20 +01:00
Alex Orlenko bf286751fa Improve code coverage 2021-06-17 00:47:15 +01:00
Alex Orlenko 3b94b4e86f Implement Hash for RegistryKey. Closes #57 2021-06-16 12:12:42 +01:00
Alex Orlenko 9f0378b77e Complitely remove the C shim 2021-06-16 22:21:45 +01:00
Alex Orlenko d3f44354e0 Revert commit ced808d5ab
I think this experiment is unsuccessful and does not work well in a module mode
with dynamic symbols resolution and mixing between different mlua instances.
Overall the Rust bug has been fixed and we can wait for the "C-unwind" feature become stable.
2021-06-16 22:13:01 +01:00
Alex Orlenko fca21d56d3 Check stack in entrypoint1 before pushing value to a stack 2021-06-13 23:30:56 +01:00
Alex Orlenko 6e52bb7e65 Fix clippy warnings && tests 2021-06-13 23:30:54 +01:00
Alex Orlenko 08ffeb0ca9 Improve module mode:
- Don't hide module function inside `luaopen_%` function.
- Raise Lua exception instead of panic if module function returns error.
2021-06-13 22:38:51 +01:00
Alex Orlenko 3b9d8a7b5f Keep all Lua symbols in unsafe mode to load C modules 2021-06-12 18:00:55 +01:00
Alex Orlenko 0fe898c0dd v0.6.0-beta.3 2021-06-04 12:38:09 +01:00
Alex Orlenko 821f1125b6 Add String::to_string_lossy 2021-06-04 00:16:40 +01:00
Alex Orlenko 6c0096d8ac Update examples 2021-06-03 23:52:29 +01:00
Alex Orlenko abb95c3c56 Remove T: Clone requirement from add_async_function 2021-06-03 23:21:00 +01:00
Alex Orlenko 64faebf407 Add __ipairs metamethod (again) for Lua 5.2 only 2021-06-03 18:43:29 +01:00
Alex Orlenko a944f4ad6f Implement UserData for Rc<RefCell>/Arc<Mutex>/Arc<RwLock> wrappers 2021-06-03 16:42:55 +01:00
Alex Orlenko bae424672a Treat errors as Send + Sync to be compatible with anyhow crate 2021-05-31 11:05:51 +01:00
Alex Orlenko 6f9eb82649 Update README 2021-05-18 20:13:28 +01:00
Alex Orlenko e8de2a458a Allow multiple entrypoints in a single module share the same Lua state.
Previously it would initialize different Lua instances.
Fixes #49.
2021-05-18 20:07:34 +01:00
Alex Orlenko 973b5c3bf5 v0.6.0-beta.2 2021-05-13 23:10:19 +01:00
Alex Orlenko b610a79d66 Update docs & minor error handling code changes 2021-05-11 14:18:22 +01:00
Alex Orlenko fe39ae09bf Try different approach for errors handling.
Instead of convering error to CallbackError in error message handler,
do it earlier at callback_error stage.
Better fix for #44.
2021-05-11 00:53:07 +01:00
Alex Orlenko 01714d2510 Prepare 0.6.0-beta.2 release 2021-05-10 22:28:33 +01:00
Alex Orlenko 0bad4a0ff9 Fix spelling 2021-05-10 19:53:38 +01:00
Alex Orlenko 35b7504076 Improve error reporting in module mode.
Attach traceback to a WrappedError.
Fixes #44.
2021-05-10 17:45:00 +01:00
Alex Orlenko c9b8eb5418 Re-export UserDataFields/UserDataMetatable 2021-05-09 09:30:33 +01:00
Alex Orlenko ef7d123f80 Exclude mlua_derive from coverage report 2021-05-06 00:41:20 +01:00
Alex Orlenko 20cba5de5b Fix clippy warnings 2021-05-06 00:36:47 +01:00
Alex Orlenko 3e03f4201c Add chunk! macro support 2021-05-05 22:13:51 +01:00
Alex Orlenko 5199b02346 Update lua state when polling futures 2021-05-05 12:37:27 +01:00
Alex Orlenko 5293b8d6d2 Add Thread::reset() for luajit/lua54 2021-05-05 11:11:32 +01:00
Alex Orlenko 7541b6f3f3 Remove deprecated AnyUserData::has_metamethod() 2021-05-04 23:57:47 +01:00
Alex Orlenko 205510a540 Update sys align code 2021-05-04 23:55:24 +01:00
Alex Orlenko 2250421438 Fix clippy warnings 2021-05-04 23:42:33 +01:00
Alex Orlenko 8e57e6fa5a v0.6.0-beta.1 2021-05-04 00:43:26 +01:00
Alex Orlenko f2dbbb091f Update dependencies 2021-05-04 00:31:51 +01:00
Alex Orlenko 13cfb4bd51 Add CHANGELOG for v0.6.0-beta.1 2021-05-03 23:36:18 +01:00
Alex Orlenko 33ebacab49 Update README 2021-05-03 22:33:18 +01:00
Alex Orlenko 3829b72212 Update code docs 2021-05-03 22:33:18 +01:00
Alex Orlenko af67971e0d Make SerializeOptions non_exhaustive.
Add builder implementation similar to `LuaOptions` to set individual options.
2021-05-03 22:33:12 +01:00
Alex Orlenko 0f4bcca7ce Add LuaOptions to customize Lua/Rust behaviour.
The only option is `catch_rust_panics` to optionally disable catching Rust panics via pcall/xpcall.
2021-05-03 21:33:49 +01:00
Alex Orlenko 585c0a25d8 Replace 'Thread already finished' error with common Error::CoroutineInactive 2021-05-03 13:11:59 +01:00
Alex Orlenko 64346ce56c Implement/Derive Debug for Lua and few other structs 2021-05-03 13:05:43 +01:00
Alex Orlenko 108682cc71 Store safe property in Extra state to inherit into Lua structs made from pointers 2021-05-03 12:40:14 +01:00
Alex Orlenko 4af7bcf0d9 Implement ToLua/FromLua for Box<str> and Box<[T]> 2021-05-03 12:22:11 +01:00
Alex Orlenko 1bb3c5c19f Fix typo in README 2021-05-02 23:53:48 +01:00
Alex Orlenko a4567cb5f7 Improve growing the auxiliary stack of the ref thread:
Try to double size first, if not fulfilled try halving in a loop till 0.
Fix unwinding after panic in ref_stack_pop.
Add test to check the stack exhaustion.
2021-05-02 23:26:02 +01:00
Alex Orlenko 26d8d899f2 Allocate Waker slot in Registry in re-use it (instead of creating new userdata for it) 2021-05-02 13:04:02 +01:00
Alex Orlenko 67bc0b1196 Make AsyncPollPending internal value as LightUserData 2021-05-02 12:06:32 +01:00
Alex Orlenko e8505b5239 Change signature of Function::dump() to remove Result 2021-05-02 11:48:42 +01:00
Alex Orlenko 3f55958bdd Stack assertions review
Other minor code and documentation updates
2021-05-02 11:42:03 +01:00
Alex Orlenko 463fc646bc Refactor UserDataCell 2021-04-27 21:55:31 +01:00
Alex Orlenko b5f1325f2f Store nonstatic UserData pointer in self userdata (instead of metatable) 2021-04-27 17:06:37 +01:00
Alex Orlenko 0625991a48 Update github action to fix modules test on Windows (lua53 -> lua54) 2021-04-27 10:38:20 +01:00
Alex Orlenko 269ef9c55d Fix lua51 scope compilation 2021-04-27 10:11:37 +01:00
Alex Orlenko f5b88624ce Hide Deserializer inner fields
Improve documentation
2021-04-27 10:04:18 +01:00
Alex Orlenko 2fae94586d Remove Result from lua.null() and lua.array_metatable(). They never fail. 2021-04-27 00:38:13 +01:00
Alex Orlenko bc81d1016f Add SerializeOptions to to change default Lua serializer behaviour 2021-04-27 00:35:53 +01:00
Alex Orlenko c19f12898d Replace lazy_static with once_cell 2021-04-27 00:29:38 +01:00
Alex Orlenko c7541ef7d3 More tests 2021-04-27 00:29:38 +01:00
Alex Orlenko 41a1a0d15a Add more checks for destructed userdata in AnyUserData 2021-04-27 00:29:38 +01:00
Alex Orlenko c10169a380 cargo fmt and other minor fixes 2021-04-27 00:29:38 +01:00
Alex Orlenko ced808d5ab Don't trigger longjmp in rust.
Motivation behind this change is upcoming breaking change in Rust
compiler v1.52.0 to prevent unwinding across FFI boundaries.
https://github.com/rust-lang/rust/pull/76570
The new functionality requires nightly compiler to declare FFI
functions as "C-unwind".
The fundamental solution is to use C shim to wrap "e" and "m"
Lua functions in pcall.
Additionally define Rust calling convention to trigger lua_error
on Rust behalf.
2021-04-27 00:29:38 +01:00
Alex Orlenko c95ac32741 Add inline(always) to some compat53 functions 2021-04-27 00:29:37 +01:00
Alex Orlenko 14169eadb1 Include nightly to github CI tests 2021-04-27 00:29:37 +01:00
Alex Orlenko 5a7ad9f7cd Fix some clippy warnings & minor changes 2021-04-27 00:29:37 +01:00
Alex Orlenko decb5b9e37 Make MetaMethod::name() public
Tests for UserDataMetatable::pairs()
2021-04-27 00:29:37 +01:00
Alex Orlenko 1635903d3f Improve/fix scoped UserData drop 2021-04-27 00:29:37 +01:00
Alex Orlenko 2b2df708f9 Add UserDataFields API.
Provide safe access to UserData metatable and allow to define custom metamethods..
2021-04-27 00:29:37 +01:00
Alex Orlenko cb1ac28f2a v0.5.4 2021-04-20 02:05:05 +01:00
Alex Orlenko 3e7f25670a Don't read lua.h from build script (was not used) 2021-04-20 02:05:00 +01:00
Alex Orlenko 0d404ce4c3 Don't fail CI on coverage 2021-04-16 22:43:21 +01:00
Alex Orlenko e26cec5db9 Drop 'feature(link_args)' (removed from nightly). Don't run tests for LuaJIT 2.0.5 2021-04-16 22:27:28 +01:00
Alex Orlenko 0bd36b42e7 Make error::Error non_exhaustive 2021-04-16 22:10:10 +01:00
Alex Orlenko e0da6ac929 Hide raw_sequence_values_by_len under async/serialize feature flags 2021-04-16 22:09:49 +01:00
Alex Orlenko 0c7db4916c Serialize only known (registered) userdata.
This reverts commit 7332c6a.
Non-static userdata is a special case and can cause segfault if try to serialize it.
Now it should be safe, plus I added non-static userdata destructor to generate better error messages
in case of accessing destructed userdata.
2021-04-16 22:01:55 +01:00
Alex Orlenko b9589491e4 Improve panic handling (check for twice resumed panics) 2021-04-15 23:04:36 +01:00
Alex Orlenko 58cb371f06 Add rerun-if-changed instructions to build script 2021-04-14 22:33:23 +01:00
Alex Orlenko 8add60b019 Don't check LUA_LIB_NAME if it's not needed 2021-04-14 22:25:49 +01:00
Alex Orlenko c363fb9288 v0.5.3 2021-03-04 00:01:06 +00:00
Alex Orlenko 3900e23839 Fix compilation warnings on nightly 2021-03-03 23:36:28 +00:00
Alex Orlenko 726fde7e1f Optimise async callbacks (polling)
call async Rust callback [sum] 3 10
                        time:   [59.338 us 59.729 us 60.097 us]
                        change: [-10.336% -8.6212% -6.8003%] (p = 0.00 < 0.05)
                        Performance has improved.
2021-03-03 23:21:56 +00:00
Alex Orlenko 7cb9c4f39c Fix bug in returning nil-prefixed multi values from async function 2021-03-03 22:32:22 +00:00
Alex Orlenko b93ace0224 v0.5.2 2021-02-27 19:32:25 +00:00
Alex Orlenko 5f37bf812d Fix/whitelist some clippy warnings 2021-02-27 19:23:08 +00:00
Alex Orlenko 1f7e760d20 Add codecov coverage report 2021-02-27 18:03:53 +00:00
Alex Orlenko 90bea4aa34 Update README and keywords 2021-02-27 13:28:47 +00:00
Alex Orlenko 7775b4a99c Bump copyright year 2021-02-26 10:35:00 +00:00
Alex Orlenko 1d9cda10eb Add ToLua implementation for Cow<str> and Cow<CStr> 2021-02-26 10:23:36 +00:00
Alex Orlenko 7332c6a28c Remove registered_userdata_mt check 2021-02-22 20:38:36 +00:00
Alex Orlenko 94670e3fdb rustfmt 2021-02-22 20:13:56 +00:00
Alex Orlenko 335f433df4 Optimize callbacks 2021-02-21 23:52:20 +00:00
Alex Orlenko 2aed548747 Fix scoped async destruction of partially polled futures 2021-02-21 23:52:07 +00:00
Alex Orlenko 6a77b5f003 Update benchmarks:
- Refactor
- Add async benchmarks
2021-02-21 18:48:45 +00:00
Alex Orlenko aeb66115f7 v0.5.1 2021-01-20 11:04:03 +00:00
Alex Orlenko ce873a40bf Update CHANGELOG 2021-01-20 11:00:47 +00:00
Alex Orlenko 8de75d1c18 Update tokio to 1.0 for async examples 2021-01-20 10:47:27 +00:00
Alex Orlenko b6ff501b8c Fix numeric types conversion for 32bit lua. Fix #27 2021-01-20 10:46:23 +00:00
Alex Orlenko 0e73ae18f4 Update CI 2021-01-16 13:32:38 +00:00
Alex Orlenko e62fd400d7 Remove unused exports from glue.{c,rs} && Fix some clippy warnings 2021-01-16 13:31:45 +00:00
Alex Orlenko 1c79f646de Update README 2021-01-16 13:31:34 +00:00
Alex Orlenko 7f5fd36a2b Merge pull request #26 from wez/cross
Support cross compilation
2021-01-14 15:35:35 +00:00
Wez Furlong faf19e4a06 Allow luajit to build in the pointer size cross compilation case 2021-01-13 20:01:12 -08:00
Wez Furlong 24d9099ef7 install more bits for cross compilation jobs 2021-01-13 10:38:08 -08:00
Wez Furlong 84003f31e7 Add CI for cross compilation cases 2021-01-13 10:18:53 -08:00
Wez Furlong e0d9ec41e2 Support cross compilation
This commit teaches the build script to recognize when it is
cross-compiling and switch to an alternative approach for generating
the `glue.rs` module.

It defaults to the equivalent logic found in the lua headers to
set the default types and parameters.

Notably: it doesn't statically produce the default lua paths as we
cannot know these without either executing the code (not guaranteed
possible when cross compiling) or regexing out the paths from the
headers (a bit brittle).  An alternative approach might be to use
something like `lazy_static` to ask the library for its compiled in
values once at runtime.

I've tested this with:

```
cargo build --target armv7-unknown-linux-gnueabihf --features lua51,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features lua52,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features lua53,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features lua54,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features luajit,vendored
```

All except luajit compile.  Luajit itself doesn't cross compile, so I
don't think we can ever reasonably get that to work.

I haven't tried to run any of this yet; my use case is actually for mac
(https://github.com/wez/wezterm/pull/426) so I need to commit this and
try patching it in over there before I can see if that truly worked
end-to-end.

refs: https://github.com/khvzak/mlua/issues/14
2021-01-13 09:55:53 -08:00
Alex Orlenko 618874ef3c v0.5.0 2020-12-31 13:39:42 +00:00
Alex Orlenko 9f82cbe0c5 Update documentation 2020-12-31 13:39:38 +00:00
Alex Orlenko 1a81f8d447 Update compile tests 2020-12-29 23:20:06 +00:00
Alex Orlenko bedd430eb5 Re-export mlua_derive 2020-12-29 22:26:15 +00:00
Alex Orlenko afc41ab23c Add serialization example & update others 2020-12-29 21:39:34 +00:00
Alex Orlenko c5d0ccc433 Add reqwest http client example to fetch json 2020-12-28 15:21:45 +00:00
Alex Orlenko ce8955f5b9 Don't try to disable c modules without package loaded. Fix #24 2020-12-22 23:05:08 +00:00
Alex Orlenko 368c4428eb Wider lua-src and luajit-src versions 2020-12-14 00:51:35 +00:00
Alex Orlenko 6e2b687cb7 Serde support (serialize feature flag) 2020-12-14 00:51:26 +00:00
Alex Orlenko 661f8e592a Update num-traits dependency
Fix f32-to-f64 conversion tests
2020-11-04 21:22:24 +00:00
Alex Orlenko c8a7aa947c Update compile tests (temporary set TRYBUILD=overwrite) 2020-10-27 23:39:52 +00:00
Alex Orlenko a52135b62d Update compile tests error messages 2020-09-28 23:24:53 +01:00
Alex Orlenko 68378a3957 Merge pull request #15 from kaikalii/master
impl `ToLua` and `FromLua` for `HashSet` and `BTreeSet`
2020-09-28 23:14:17 +01:00
Kai Schmidt c65058a40a impl ToLua and FromLua for HashSet and BTreeSet 2020-09-27 17:54:47 -07:00
Alex Orlenko 93d63cef35 v0.4.2 2020-08-17 12:17:08 +01:00
Alex Orlenko b743245aba Update CHANGELOG 2020-08-17 11:14:40 +01:00
Alex Orlenko a172c021c9 Update set_memory_limit doc 2020-07-30 12:16:25 +01:00
Alex Orlenko de2c5cd9a9 Fix compilation warnings on nightly rust 2020-07-28 21:10:55 +01:00
Alex Orlenko d201beadc9 Add ChunkMode enum to mark chunks as text or binary 2020-07-28 21:04:21 +01:00
Alex Orlenko dd58cdad52 Add Function::dump() to dump lua function to a binary chunk 2020-07-27 23:26:33 +01:00
Alex Orlenko 5c8a5e0a5a Merge pull request #9 from HybridEidolon/bytecode-chunks
Make Lua::load load binary chunks when unsafe
2020-07-27 14:19:33 +01:00
Alex Orlenko e07c53eafe Update compile tests (2) 2020-07-27 13:51:21 +01:00
Alex Orlenko ad619390e1 Run compile tests on macos (was ubuntu-18.04) 2020-07-27 11:34:48 +01:00
Alex Orlenko 350602ab6e Update lua-src dependency to 5.4.0 2020-07-27 10:52:28 +01:00
Alex Orlenko 4b1bc88273 Update compile tests 2020-07-27 10:49:01 +01:00
Eidolon 883bf082b9 Make Lua::load load binary chunks when unsafe 2020-07-27 03:37:38 -05:00
Alex Orlenko 4265250cfd 0.4.1 release
Fix docs.rs build features
Update Cargo.toml description
2020-06-08 14:08:07 +01:00
Alex Orlenko 1d9fed2207 0.4.0 release 2020-06-08 02:28:26 +01:00
Alex Orlenko a67cbafff1 Run LuaJIT 2.0.5 tests in 1 thread
Seems there is a race in a LuaJIT 2.0 that makes the tests unstable.
Works ok on LuaJIT 2.1 and all Lua.
2020-06-08 02:19:53 +01:00
Alex Orlenko 39bcb19ae7 Update dependencies 2020-06-08 00:22:53 +01:00
Alex Orlenko efcaef3db7 Enable hooks support for LuaJIT 2020-06-07 20:38:19 +01:00
Alex Orlenko 0a13a9631d Fix LuaJIT 2.1 libraries loading 2020-06-07 20:38:19 +01:00
Alex Orlenko 3d42bc4ca6 Refactor main_state handling
Don't allow to set hook if main_state is not available
Remove Lua 5.1 dirty hack
2020-06-07 20:38:19 +01:00
Alex Orlenko 2eb40deafd Add test to check loading module from lua coroutine (thread) 2020-06-07 20:38:19 +01:00
Alex Orlenko 2595fe9885 Update CHANGELOG 2020-06-07 20:38:19 +01:00
Alex Orlenko 4865089a78 Update README 2020-06-07 20:38:19 +01:00
Alex Orlenko a35b8dda68 Raise compile_error! instead of panic! in the build script 2020-06-07 20:38:19 +01:00
Alex Orlenko 95b9fe8dac Fix clippy warnings 2020-06-07 20:38:19 +01:00
Alex Orlenko 5952a1f709 New module feature
Don't link module with Lua core (see: http://lua-users.org/wiki/BuildingModules)
Example and tests for modules
2020-06-07 20:38:11 +01:00
Alex Orlenko 73c7c94d75 Remove default feature lua53 2020-06-06 16:07:16 +01:00
Alex Orlenko d5c22d989a Dirty hack to get lua 5.1 main state 2020-06-06 16:07:16 +01:00
Alex Orlenko a9a4cf13f1 Add has_metamethod to AnyUserData 2020-06-06 16:07:16 +01:00
Alex Orlenko c3822219e0 Add hooks support (based on rlua v0.17 implementation)
This feature works on lua54, lua53, lua52 and lua51 only.
LuaJIT is unstable.
2020-06-06 16:07:16 +01:00
Alex Orlenko f6da437d8b Update docs 2020-06-06 16:07:16 +01:00
Alex Orlenko 2fd6757f39 Add LuaJIT 2.0.5 stable support 2020-06-06 16:07:16 +01:00
Alex Orlenko 56407fac0d Update CHANGELOG 2020-06-06 16:07:13 +01:00
Alex Orlenko d0fa03e9c8 Bump version to 0.4.0-beta.1 2020-06-06 16:05:35 +01:00
Alex Orlenko cccec914ca Remove deprecated Table::call 2020-05-15 01:48:57 +01:00
Alex Orlenko 687ecc9247 Update documentation 2020-05-15 01:48:57 +01:00
Alex Orlenko 79bfb112aa Remove redundant lifetimes 2020-05-15 01:48:57 +01:00
Alex Orlenko 60c659ecff Add async http server example 2020-05-15 01:48:57 +01:00
Alex Orlenko 9f2d598517 Add clippy check and fix clippy warnings 2020-05-15 01:48:56 +01:00
Alex Orlenko 1492790ece Update compile tests to include async/send features 2020-05-12 03:16:29 +01:00
Alex Orlenko e88f086ed8 Update github workflow 2020-05-12 02:14:48 +01:00
Alex Orlenko 5a9a308790 Provide safe and unsafe Lua modes:
- In safe mode Lua would not have ability to load C code via `require` or `package.loadlib`
- Unsafe mode allows everything.
2020-05-12 02:14:48 +01:00
Alex Orlenko 1b2b94c808 Use Rust allocator for new Lua states that allows to set memory limit 2020-05-12 02:14:48 +01:00
Alex Orlenko 526e7418d8 Add MetaMethod::Close to support Lua 5.4 to-be-closed variables 2020-05-12 02:14:47 +01:00
Alex Orlenko 24d4f04c0d Add Lua 5.4 to github workflow 2020-05-11 02:43:34 +01:00
Alex Orlenko 539b569ff4 Add Lua 5.4 (rc2) support 2020-05-11 02:43:34 +01:00
Alex Orlenko 5c226b4915 Lua 5.4 FFI 2020-05-11 02:43:34 +01:00
Alex Orlenko d366ce0dd4 Scope support (including async) 2020-05-11 02:43:34 +01:00
Alex Orlenko 2bd5c2f6ca Hide Lua "Send" capability under the optional "send" feature flag 2020-05-11 02:43:34 +01:00
Alex Orlenko 7b0e4b4280 Add Send capability to Lua 2020-05-11 02:43:34 +01:00
Alex Orlenko 6e2bb73cff Bind Futures lifetimes to 'lua rather than 'static.
Fix async examples.
2020-05-11 02:43:34 +01:00
Alex Orlenko 7efcee853d Don't allow 'callback outlive 'lua 2020-05-11 02:43:33 +01:00
Alex Orlenko e9c2b8d306 0.3.2 release 2020-04-29 16:32:16 +01:00
Alex Orlenko d672e19365 Add ToLua implementation for slices and arrays 2020-04-29 16:12:58 +01:00
Alex Orlenko bda399a5b4 Get yield function in the moment of async callback creation.
This is usefull to temporary override `coroutine.yield` prior to create_async_callback()
and then restore to original value.
2020-04-28 14:29:36 +01:00
Alex Orlenko fe5e87b0f5 Fix bugs with metatable cache:
- Don't use thread_local as Lua can be created in one thread and executed in another (in module mode);
- Make per state storage without luaL_ref;
2020-04-28 14:29:14 +01:00
Alex Orlenko 0f32e9cb43 0.3.1 release 2020-04-20 01:55:57 +01:00
Alex Orlenko 0efa0fcb6a Update documentation 2020-04-20 01:52:01 +01:00
Alex Orlenko 4e19ae6ccf Update tests (async and table) 2020-04-20 01:14:34 +01:00
Alex Orlenko c826798a6d Minor refactor 2020-04-19 16:51:35 +01:00
Alex Orlenko ee08050c1f Add TableExt trait with call_method/function methods 2020-04-19 16:15:16 +01:00
Alex Orlenko d8897d867b Update examples 2020-04-19 01:23:42 +01:00
Alex Orlenko 222f4df668 Add family of call_async function
Update documentation
Move async tests to a separate file
2020-04-18 21:26:12 +01:00
Alex Orlenko afaa0eb639 0.3.0 release 2020-04-17 23:55:58 +01:00
Alex Orlenko db5ad6bc60 Include async build/test to github workflow 2020-04-17 23:55:58 +01:00
Alex Orlenko ef06c5eec9 Update Cargo.toml 2020-04-17 23:55:57 +01:00
Alex Orlenko f7dc9da107 Update README
Cargo fmt and minor changes
2020-04-17 22:52:34 +01:00
Alex Orlenko 47e8a80c1c v0.3.0-alpha.1 with async support
Squashed commit of the async branch.
2020-04-17 22:39:50 +01:00
Alex Orlenko 1a788c48f1 Cherry-pick changes from rlua:
- Make Value::type_name() public
- Update CallbackError and ExternalError Display impl
2020-04-15 21:23:00 +01:00
Alex Orlenko d8b9c46d28 0.2.2 release 2020-02-09 22:21:34 +00:00
Alex Orlenko 9bd6d6fa92 Fix compile fail tests 2020-02-09 22:09:42 +00:00
Alex Orlenko ea6e6e00b2 Update README.md 2020-02-09 21:48:50 +00:00
Alex Orlenko 03ed7f6f99 Fix compile test 2020-01-27 21:57:12 +00:00
Alex Orlenko d5bc9c8c24 Combine lua-vendored and luajit-vendored features into "vendored" 2020-01-27 21:21:50 +00:00
Alex Orlenko 5cd0b645c4 Merge branch 'github_actions' 2020-01-25 23:07:31 +00:00
Alex Orlenko bf36eb72f8 Move from circele-ci to github actions 2020-01-25 22:35:47 +00:00
Alex Orlenko 1f7f8f62ab Merge branch 'lua_vendored' 2020-01-25 20:49:00 +00:00
Alex Orlenko e4dc773aa3 Remove __ipairs metamethod deprecated in lua 5.3 and not available by default 2020-01-25 20:47:36 +00:00
Alex Orlenko 07fc4642ae Support vendored versions of Lua and LuaJIT 2020-01-25 20:47:31 +00:00
Alex Orlenko 27121c779d Add raw_insert() and raw_remove() for tables (represented as lists) 2020-01-07 20:53:47 +00:00
Alex Orlenko 5eec0ef56b Implement PartialEq trait for Value (and subtypes)
Add equals() method to compare values optionally invoking __eq.
2020-01-07 00:03:03 +00:00
Alex Orlenko 831161bfda Fix CI badge link 2020-01-06 23:55:56 +00:00
Alex Orlenko 52be96677b 0.2.1 release 2019-12-26 23:09:11 +00:00
Alex Orlenko 42b33849e1 Add support of loading a specified set of standard libraries 2019-12-26 23:04:21 +00:00
138 changed files with 20116 additions and 4682 deletions
-49
View File
@@ -1,49 +0,0 @@
version: 2
jobs:
build:
docker:
- image: circleci/rust:latest
steps:
- checkout
- run:
name: Version information
command: rustc --version; cargo --version; rustup --version
- run:
name: Calculate dependencies
command: cargo generate-lockfile
- restore_cache:
keys:
- cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Check Formatting
command: |
rustup component add rustfmt
rustfmt --version
cargo fmt --all -- --check --color=auto
- run:
name: Install Lua
command: |
sudo apt-get update
sudo apt-get -y --no-install-recommends install liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
- run:
name: Build all targets
command: cargo build --all --all-targets
- run:
name: Run all tests / Lua 5.3
command: cargo test --all --no-default-features --features lua53
- run:
name: Run all tests / Lua 5.2
command: cargo test --all --no-default-features --features lua52
- run:
name: Run all tests / Lua 5.1
command: cargo test --all --no-default-features --features lua51
- run:
name: Run all tests / LuaJIT
command: cargo test --all --no-default-features --features luajit
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
key: cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
+1
View File
@@ -0,0 +1 @@
github: khvzak
+23
View File
@@ -0,0 +1,23 @@
name: coverage
on: [push]
jobs:
test:
name: coverage
runs-on: ubuntu-latest
container:
image: xd009642/tarpaulin
options: --security-opt seccomp=unconfined
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Generate code coverage
run: |
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
- name: Upload to codecov.io
uses: codecov/codecov-action@v1
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: false
+242
View File
@@ -0,0 +1,242 @@
name: CI
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-20.04
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
- uses: Swatinem/rust-cache@v1
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --features "${{ matrix.lua }},vendored"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-20.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 --features "${{ matrix.lua }}"
build_aarch64_cross_macos:
name: Cross-compile to aarch64-apple-darwin
runs-on: macos-11.0
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: aarch64-apple-darwin
override: true
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
runs-on: ubuntu-20.04
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
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,parking_lot"
shell: bash
build_armv7_cross_ubuntu:
name: Cross-compile to armv7-unknown-linux-gnueabihf
runs-on: ubuntu-20.04
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
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,parking_lot"
shell: bash
test:
name: Test
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
include:
- os: ubuntu-20.04
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
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --features "${{ matrix.lua }},vendored"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
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,parking_lot" -- --ignored
shell: bash
test_with_sanitizer:
name: Test with address sanitizer
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-20.04]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-20.04
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
RUSTFLAGS="-Z sanitizer=address" \
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
test_modules:
name: Test modules
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-20.04, macos-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit]
include:
- os: ubuntu-20.04
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
- uses: Swatinem/rust-cache@v1
- 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
test_modules_windows:
name: Test modules on Windows
runs-on: windows-latest
needs: build
strategy:
matrix:
lua: [lua54, luajit]
defaults:
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 }}")
rustfmt:
name: Rustfmt
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
override: true
- run: cargo fmt -- --check
clippy:
name: Clippy check
runs-on: ubuntu-20.04
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit, luau]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
+211
View File
@@ -0,0 +1,211 @@
## 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
- Roblox 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
- Full access to `UserData` metatables with support of setting arbitrary fields.
- Implement `UserData` for `Rc<RefCell<T>>`/`Arc<Mutex<T>>`/`Arc<RwLock<T>>` where `T: UserData`.
- Added `SerializeOptions` to to change default Lua serializer behaviour (eg. `nil/null/array` serialization)
- 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 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
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()` (in favour of full access to metatables)
## v0.6.0-beta.3
- Errors are always `Send + Sync` to be compatible with anyhow crate
- Implement `UserData` for `Rc<RefCell>`/`Arc<Mutex>`/`Arc<RwLock>`
- Added `__ipairs` metamethod for Lua 5.2
- Added `String::to_string_lossy`
- Various bugfixes and improvements
## v0.6.0-beta.2
- [**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.
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
## v0.6.0-beta.1
- New `UserDataFields` API
- Allow to define arbitrary MetaMethods
- `MetaMethods::name()` is public
- Do not trigger longjmp in Rust to prevent unwinding across FFI boundaries. See https://github.com/rust-lang/rust/issues/83541
- Added `SerializeOptions` to to change default Lua serializer behaviour (eg. nil/null/array serialization)
- [**Breaking**] Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
- [**Breaking**] Removed `Result` from `Function::dump()` (never fails)
- `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`
- [**Breaking**] Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
- Various bugfixes and performance improvements
## v0.5.4
- Build script improvements
- Improvements in panic handling (resume panic on value popping)
- Fixed bug serializing 3rd party userdata (causes segfault)
- Make error::Error non exhaustive
## v0.5.3
- Fixed bug when returning nil-prefixed multi values from async function (+ test)
- Performance optimisation for async callbacks (polling)
## v0.5.2
- Some performance optimisations (callbacks)
- `ToLua` implementation for `Cow<str>` and `Cow<CStr>`
- Fixed bug with `Scope` destruction of partially polled futures
## v0.5.1
- Support cross compilation that should work well for vendored builds (including LuaJIT with some restrictions)
- Fix numeric types conversion for 32bit Lua
- Update tokio to 1.0 for async examples
## v0.5.0
- Serde support under `serialize` feature flag.
- Re-export `mlua_derive`.
- impl `ToLua` and `FromLua` for `HashSet` and `BTreeSet`
## v0.4.2
- Added `Function::dump()` to dump lua function to a binary chunk
- Added `ChunkMode` enum to mark chunks as text or binary
- Updated `set_memory_limit` doc
## v0.4.0
- 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()`.
- 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).
- Added `Lua::into_static()` to make `'static` Lua state. This is useful to spawn async Lua threads that requires `'static`.
- New function `Lua::set_memory_limit()` (similar to `rlua`) to enable memory restrictions in Lua VM (requires Lua >= 5.2).
- `Scope`, temporary removed in v0.3, is back with async support.
- Removed deprecated `Table::call()` function.
- Added hooks support (backported from rlua 0.17).
- New `AnyUserData::has_metamethod()` function.
- LuaJIT 2.0.5 (the latest stable) support.
- Various bug fixes and improvements.
+75 -12
View File
@@ -1,47 +1,110 @@
[package]
name = "mlua"
version = "0.2.0"
version = "0.8.0-beta.4" # 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"
documentation = "https://docs.rs/mlua"
readme = "README.md"
keywords = ["lua", "luajit"]
categories = ["api-bindings"]
keywords = ["lua", "luajit", "async", "futures", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT)
with support of writing native lua modules in Rust.
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
with async/await features and support of writing native Lua modules in Rust.
"""
[badges]
travis-ci = { repository = "khvzak/mlua", branch = "master" }
[package.metadata.docs.rs]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"examples/module",
"tests/module",
]
[features]
default = ["lua53"]
lua54 = []
lua53 = []
lua52 = []
lua51 = []
luajit = []
luajit52 = ["luajit"]
luau = ["luau0-src"]
vendored = ["lua-src", "luajit-src"]
module = ["mlua_derive"]
async = ["futures-core", "futures-task", "futures-util"]
send = []
serialize = ["serde", "erased-serde"]
macros = ["mlua_derive/macros"]
[dependencies]
num-traits = { version = "0.2.6" }
mlua_derive = { version = "=0.8.0-beta.1", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
rustc-hash = "1.0"
futures-core = { version = "0.3.5", optional = true }
futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true }
serde = { version = "1.0", optional = true }
erased-serde = { version = "0.3", optional = true }
parking_lot = { version = "0.12", optional = true }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.11" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
luau0-src = { version = "0.2.2", optional = true }
[dev-dependencies]
rustyline = "5.0"
criterion = "0.2.0"
rustyline = "9.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 = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
maplit = "1.0"
tempfile = "3"
[[bench]]
name = "benchmark"
harness = false
required-features = ["async"]
[[example]]
name = "async_http_client"
required-features = ["async", "macros"]
[[example]]
name = "async_http_reqwest"
required-features = ["async", "serialize", "macros"]
[[example]]
name = "async_http_server"
required-features = ["async", "macros"]
[[example]]
name = "async_tcp_server"
required-features = ["async", "macros"]
[[example]]
name = "guided_tour"
required-features = ["macros"]
[[example]]
name = "serialize"
required-features = ["serialize"]
[[example]]
name = "userdata"
required-features = ["macros"]
+1 -1
View File
@@ -3,7 +3,7 @@ below:
MIT License
Copyright (c) 2019 A. Orlenko
Copyright (c) 2019-2021 A. Orlenko
Copyright (c) 2017 rlua
Permission is hereby granted, free of charge, to any person obtaining a copy
+151 -36
View File
@@ -1,29 +1,112 @@
# mlua
[![Build Status]][circleci] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
[Build Status]: https://img.shields.io/circleci/project/github/khvzak/mlua.svg
[circleci]: https://circleci.com/gh/khvzak/mlua
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
[github-actions]: https://github.com/khvzak/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
[MSRV]: https://img.shields.io/badge/rust-1.53+-brightgreen.svg?&logo=rust
[Guided Tour](examples/guided_tour.rs)
A fork of [rlua 0.15](https://github.com/kyren/rlua/tree/0.15.3) which provides a high level
interface between Rust and Lua. Unlike `rlua` , `mlua` supports Lua 5.1 (including LuaJIT), 5.2 and 5.3.
The `mlua` goal is to be an easy to use, practical and flexible API between Rust and Lua but
*__not__* always 100% safe due to the Lua VM nature. Also, `mlua` provides a way to write native lua
modules in Rust.
`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.
### Usage
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Roblox 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
[Roblox Luau]: https://luau-lang.org
## Usage
### Feature flags
`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
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
* `luau`: activate [Luau] support (auto vendored mode)
* `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`)
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
* `macros`: enable procedural macros (such as `chunk!`)
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
[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/
[Luau]: https://github.com/Roblox/luau
[lua-src]: https://github.com/khvzak/lua-src-rs
[luajit-src]: https://github.com/khvzak/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
[serde]: https://github.com/serde-rs/serde
[parking_lot]: https://github.com/Amanieu/parking_lot
### Async/await support
`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)
- [HTTP Client (json)](examples/async_http_reqwest.rs)
- [HTTP Server](examples/async_http_server.rs)
- [TCP Server](examples/async_tcp_server.rs)
### 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).
[Example](examples/serialize.rs)
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
[`mlua::Value`]: https://docs.rs/mlua/latest/mlua/enum.Value.html
### Compiling
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.
`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
```
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
[luajit-src](https://crates.io/crates/luajit-src).
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
### Standalone mode
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
#### standalone mode
Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = "0.2"
mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -46,7 +129,10 @@ fn main() -> LuaResult<()> {
}
```
#### module mode
### 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.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)
Add to `Cargo.toml` :
@@ -55,15 +141,12 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = "0.2"
mlua_derive = "0.2"
mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored", "module"] }
```
`lib.rs` :
``` rust
#[macro_use]
extern crate mlua_derive;
use mlua::prelude::*;
fn hello(_: &Lua, name: String) -> LuaResult<()> {
@@ -71,7 +154,7 @@ fn hello(_: &Lua, name: String) -> LuaResult<()> {
Ok(())
}
#[lua_module]
#[mlua::lua_module]
fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("hello", lua.create_function(hello)?)?;
@@ -79,32 +162,62 @@ fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
}
```
And then (macos example):
And then (**macOS** example):
``` sh
$ cargo build
$ 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:
``` toml
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
```
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.
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
### 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:
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
## Safety
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
Every place where the Lua C API may trigger an error longjmp
in any way is protected by `lua_pcall` , and the user of the library is protected
from directly interacting with unsafe things like the Lua stack, and there is
overhead associated with this safety.
Every place where the Lua C API may trigger an error longjmp in any way is protected by `lua_pcall`,
and the user of the library is protected from directly interacting with unsafe things like the Lua stack,
and there is overhead associated with this safety.
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
This library contains a huge amount of unsafe code. There are almost
certainly bugs still lurking in this library! It is surprisingly, fiendishly
difficult to use the Lua C API without the potential for unsafety.
This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
It is surprisingly, fiendishly difficult to use the Lua C API without the potential for unsafety.
## Panic handling
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
resumed then by propagating the Lua error to Rust code.
resumed then by returning or propagating the Lua error to Rust code.
For example:
``` rust
@@ -123,24 +236,26 @@ let _ = lua.load(r#"
unreachable!()
```
`mlua` should also be panic safe in another way as well, which is that any `Lua`
instances or handles remains usable after a user generated panic, and such
panics should not break internal invariants or leak Lua stack space. This is
mostly important to safely use `mlua` types in Drop impls, as you should not be
Optionally `mlua` can disable Rust panics catching in Lua via `pcall`/`xpcall` and automatically resume
them across the Lua API boundary. This is controlled via `LuaOptions` and done by wrapping the Lua `pcall`/`xpcall`
functions on a way to prevent catching errors that are wrapped Rust panics.
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
remains usable after a user generated panic, and such panics should not break internal invariants or
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
using panics for general error handling.
Below is a list of `mlua` behaviors that should be considered a bug.
If you encounter them, a bug report would be very welcome:
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
+ If you can cause UB with `mlua` without typing the word "unsafe", this is a bug.
+ The above is true even for the internal panic about running out of stack space! There are a few ways to generate normal script errors by running out of stack, but if you encounter a *panic* based on running out of stack, this is a bug.
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
+ Lua C API errors are handled by lonjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
+ Lua C API errors are handled by longjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
+ 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.
## License
This project is licensed under the [MIT license](LICENSE)
+221 -113
View File
@@ -1,22 +1,22 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use std::time::Duration;
use tokio::runtime::Runtime;
use tokio::task;
use mlua::prelude::*;
fn collect_gc_twice(lua: &Lua) {
lua.gc_collect().unwrap();
lua.gc_collect().unwrap();
}
fn create_table(c: &mut Criterion) {
c.bench_function("create table", |b| {
b.iter_batched_ref(
|| Lua::new(),
|lua| {
let lua = Lua::new();
c.bench_function("create [table empty]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
lua.create_table().unwrap();
},
BatchSize::SmallInput,
@@ -25,12 +25,14 @@ fn create_table(c: &mut Criterion) {
}
fn create_array(c: &mut Criterion) {
c.bench_function("create array 10", |b| {
b.iter_batched_ref(
|| Lua::new(),
|lua| {
let lua = Lua::new();
c.bench_function("create [array] 10", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table = lua.create_table().unwrap();
for i in 1..11 {
for i in 1..=10 {
table.set(i, i).unwrap();
}
},
@@ -40,10 +42,12 @@ fn create_array(c: &mut Criterion) {
}
fn create_string_table(c: &mut Criterion) {
c.bench_function("create string table 10", |b| {
b.iter_batched_ref(
|| Lua::new(),
|lua| {
let lua = Lua::new();
c.bench_function("create [table string] 10", |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();
@@ -55,30 +59,15 @@ fn create_string_table(c: &mut Criterion) {
});
}
fn call_add_function(c: &mut Criterion) {
c.bench_function("call add function 3 10", |b| {
b.iter_batched_ref(
|| {
let lua = Lua::new();
let f = {
let f: LuaFunction = lua
.load(
r#"
function(a, b, c)
return a + b + c
end
"#,
)
.eval()
.unwrap();
lua.create_registry_value(f).unwrap()
};
(lua, f)
},
|(lua, f)| {
let add_function: LuaFunction = lua.registry_value(f).unwrap();
fn create_function(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [function] 10", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
for i in 0..10 {
let _result: i64 = add_function.call((i, i + 1, i + 2)).unwrap();
lua.create_function(move |_, ()| Ok(i)).unwrap();
}
},
BatchSize::SmallInput,
@@ -86,72 +75,97 @@ fn call_add_function(c: &mut Criterion) {
});
}
fn call_add_callback(c: &mut Criterion) {
c.bench_function("call callback add 2 10", |b| {
fn call_lua_function(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("call Lua function [sum] 3 10", |b| {
b.iter_batched_ref(
|| {
let lua = Lua::new();
let f = {
let c: LuaFunction = lua
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
.unwrap();
lua.globals().set("callback", c).unwrap();
let f: LuaFunction = lua
.load(
r#"
function()
for i = 1,10 do
callback(i, i, i)
end
end
"#,
)
.eval()
.unwrap();
lua.create_registry_value(f).unwrap()
};
(lua, f)
collect_gc_twice(&lua);
lua.load("function(a, b, c) return a + b + c end")
.eval::<LuaFunction>()
.unwrap()
},
|(lua, f)| {
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
entry_function.call::<_, ()>(()).unwrap();
|function| {
for i in 0..10 {
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
}
},
BatchSize::SmallInput,
);
});
}
fn call_append_callback(c: &mut Criterion) {
c.bench_function("call callback append 10", |b| {
fn call_sum_callback(c: &mut Criterion) {
let lua = Lua::new();
let callback = 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(
|| {
let lua = Lua::new();
let f = {
let c: LuaFunction = lua
.create_function(|_, (a, b): (LuaString, LuaString)| {
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
})
.unwrap();
lua.globals().set("callback", c).unwrap();
let f: LuaFunction = lua
.load(
r#"
function()
for _ = 1,10 do
callback("a", "b")
end
end
"#,
)
.eval()
.unwrap();
lua.create_registry_value(f).unwrap()
};
(lua, f)
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
.eval::<LuaFunction>()
.unwrap()
},
|(lua, f)| {
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
entry_function.call::<_, ()>(()).unwrap();
|function| {
function.call::<_, ()>(()).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_async_sum_callback(c: &mut Criterion) {
let options = LuaOptions::new().thread_cache_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let callback = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
task::yield_now().await;
Ok(a + b + c)
})
.unwrap();
lua.globals().set("callback", callback).unwrap();
c.bench_function("call async Rust callback [sum] 3 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 callback(i, i+1, i+2) end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| async move {
function.call_async::<_, ()>(()).await.unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_concat_callback(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();
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();
},
BatchSize::SmallInput,
);
@@ -159,10 +173,12 @@ fn call_append_callback(c: &mut Criterion) {
}
fn create_registry_values(c: &mut Criterion) {
c.bench_function("create registry 10", |b| {
b.iter_batched_ref(
|| Lua::new(),
|lua| {
let lua = Lua::new();
c.bench_function("create [registry value] 10", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
for _ in 0..10 {
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
}
@@ -177,10 +193,12 @@ fn create_userdata(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {}
c.bench_function("create userdata 10", |b| {
b.iter_batched_ref(
|| Lua::new(),
|lua| {
let lua = Lua::new();
c.bench_function("create [table userdata] 10", |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();
@@ -191,20 +209,110 @@ fn create_userdata(c: &mut Criterion) {
});
}
fn call_userdata_index(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, index: String| Ok(index));
}
}
let lua = Lua::new();
lua.globals().set("userdata", UserData(10)).unwrap();
c.bench_function("call [userdata index] 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do local v = userdata.test end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
function.call::<_, ()>(()).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_userdata_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("method", |_, this, ()| Ok(this.0));
}
}
let lua = Lua::new();
lua.globals().set("userdata", UserData(10)).unwrap();
c.bench_function("call [userdata method] 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do userdata:method() end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
function.call::<_, ()>(()).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_async_userdata_method(c: &mut Criterion) {
#[derive(Clone, Copy)]
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) });
}
}
let options = LuaOptions::new().thread_cache_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
lua.globals().set("userdata", UserData(10)).unwrap();
c.bench_function("call async [userdata 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()
},
|function| async move {
function.call_async::<_, ()>(()).await.unwrap();
},
BatchSize::SmallInput,
);
});
}
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(200)
.sample_size(300)
.measurement_time(Duration::from_secs(10))
.noise_threshold(0.02);
targets =
create_table,
create_array,
create_string_table,
call_add_function,
call_add_callback,
call_append_callback,
create_function,
call_lua_function,
call_sum_callback,
call_async_sum_callback,
call_concat_callback,
create_registry_values,
create_userdata,
call_userdata_index,
call_userdata_method,
call_async_userdata_method,
}
criterion_main!(benches);
-189
View File
@@ -1,189 +0,0 @@
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Error, ErrorKind, Result};
use std::ops::Bound;
use std::path::{Path, PathBuf};
use std::process::Command;
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 use_custom_lua<S: AsRef<str>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
let mut version_found = String::new();
// Find LUA_VERSION_NUM
let mut lua_h_path = PathBuf::from(include_dir.as_ref());
lua_h_path.push("lua.h");
let f = File::open(lua_h_path)?;
let reader = BufReader::new(f);
for line in reader.lines() {
let line = line?;
let parts = line.split_whitespace().collect::<Vec<_>>();
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
version_found = parts[2].to_string();
}
}
let mut static_link = "";
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" {
static_link = "static=";
}
println!("cargo:rustc-link-search=native={}", lib_dir.as_ref());
println!("cargo:rustc-link-lib={}{}", static_link, lua_lib.as_ref());
Ok(version_found)
}
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_paths: &[P]) {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
// Ensure the presence of glue.rs
// if build_dir.join("glue.rs").exists() {
// return;
// }
let mut config = cc::Build::new();
for path in include_paths {
config.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();
}
fn main() {
let include_dir = env::var("LUA_INC").unwrap_or(String::new());
let lib_dir = env::var("LUA_LIB").unwrap_or(String::new());
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or(String::new());
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");
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
if include_dir != "" && lib_dir != "" && lua_lib != "" {
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
build_glue(&[include_dir]);
return;
}
// Find lua via pkg-config
#[cfg(not(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)))]
panic!("You must enable one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(
feature = "lua53",
any(feature = "lua52", feature = "lua51", feature = "luajit")
))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(feature = "lua53")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.3");
}
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(err) => panic!(err),
};
}
#[cfg(feature = "lua52")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.2");
}
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(_) => panic!("Lua 5.2 not found"),
};
}
#[cfg(feature = "lua51")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.1");
}
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(err) => panic!(err),
};
}
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.0.5"), Bound::Unbounded))
.probe("luajit");
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(err) => panic!(err),
};
}
}
+5
View File
@@ -0,0 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
unreachable!()
}
+91
View File
@@ -0,0 +1,91 @@
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() -> Option<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 Some(PathBuf::from(include_dir));
}
// Find using `pkg-config`
#[cfg(feature = "lua54")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", "lua5.4", "5.4");
#[cfg(feature = "lua53")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", "lua5.3", "5.3");
#[cfg(feature = "lua52")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", "lua5.2", "5.2");
#[cfg(feature = "lua51")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", "lua5.1", "5.1");
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51"
))]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe(alt_probe);
}
lua.unwrap_or_else(|_| panic!("cannot find Lua {} using `pkg-config`", ver))
.include_paths
.get(0)
.cloned()
}
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.0.4"), Bound::Unbounded))
.cargo_metadata(need_lua_lib)
.probe("luajit");
lua.expect("cannot find LuaJIT using `pkg-config`")
.include_paths
.get(0)
.cloned()
}
}
+27
View File
@@ -0,0 +1,27 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
#[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 = {
let mut builder = luajit_src::Build::new();
if cfg!(feature = "luajit52") {
builder.lua52compat(true);
}
builder.build()
};
#[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new().build();
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata();
Some(artifacts.include_dir().to_owned())
}
+111
View File
@@ -0,0 +1,111 @@
#[cfg_attr(
any(
feature = "luau",
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",
feature = "luau"
)),
path = "find_dummy.rs"
)]
mod find;
fn main() {
#[cfg(not(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)))]
compile_error!(
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua54",
any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua53",
any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
)
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(
feature = "lua52",
any(feature = "lua51", feature = "luajit", feature = "luau")
))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(feature = "lua51", any(feature = "luajit", feature = "luau")))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
#[cfg(all(feature = "luajit", feature = "luau"))]
compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
);
// 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."
);
find::probe_lua();
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
+68
View File
@@ -0,0 +1,68 @@
use std::collections::HashMap;
use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient;
use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods};
struct BodyReader(HyperBody);
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
let mut reader = reader.borrow_mut::<Self>()?;
if let Some(bytes) = reader.0.data().await {
let bytes = bytes.to_lua_err()?;
return Some(lua.create_string(&bytes)).transpose();
}
Ok(None)
});
}
}
#[tokio::main]
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 lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
let mut headers = HashMap::new();
for (key, value) in resp.headers() {
headers
.entry(key.as_str())
.or_insert(Vec::new())
.push(value.to_str().to_lua_err()?);
}
lua_resp.set("headers", headers)?;
lua_resp.set("body", BodyReader(resp.into_body()))?;
Ok(lua_resp)
})?;
let f = lua
.load(chunk! {
local res = $fetch_url(...)
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)
end
until not body
})
.into_function()?;
f.call_async("http://httpbin.org/ip").await
}
+35
View File
@@ -0,0 +1,35 @@
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
#[tokio::main]
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()?;
lua.to_value(&json)
})?;
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)
})
.into_function()?;
f.call_async("https://httpbin.org/anything?arg0=val0").await
}
+136
View File
@@ -0,0 +1,136 @@
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use hyper::server::conn::AddrStream;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};
use mlua::{
chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods,
};
struct LuaRequest(SocketAddr, Request<Body>);
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()));
}
}
pub struct Svc(Rc<Lua>, SocketAddr);
impl Service<Request<Body>> for Svc {
type Response = Response<Body>;
type Error = LuaError;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
// If handler returns an error then generate 5xx response
let lua = self.0.clone();
let lua_req = LuaRequest(self.1, req);
Box::pin(async move {
let handler: Function = lua.named_registry_value("http_handler")?;
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.as_bytes());
}
}
let body = lua_resp
.get::<_, Option<LuaString>>("body")?
.map(|b| Body::from(b.as_bytes().to_vec()))
.unwrap_or_else(Body::empty);
Ok(resp.body(body).unwrap())
}
Err(err) => {
eprintln!("{}", err);
Ok(Response::builder()
.status(500)
.body(Body::from("Internal Server Error"))
.unwrap())
}
}
})
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let lua = Rc::new(Lua::new());
// Create Lua handler function
let handler: Function = lua
.load(chunk! {
function(req)
return {
status = 200,
headers = {
["X-Req-Method"] = req:method(),
["X-Remote-Addr"] = req:remote_addr(),
},
body = "Hello from Lua!\n"
}
end
})
.eval()
.expect("cannot create Lua handler");
// Store it in the Registry
lua.set_named_registry_value("http_handler", handler)
.expect("cannot store Lua handler");
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua));
println!("Listening on http://{}", addr);
// Create `LocalSet` to spawn !Send futures
let local = tokio::task::LocalSet::new();
local.run_until(server).await.expect("cannot run server")
}
struct MakeSvc(Rc<Lua>);
impl Service<&AddrStream> for MakeSvc {
type Response = Svc;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: &AddrStream) -> Self::Future {
let lua = self.0.clone();
let remote_addr = stream.remote_addr();
Box::pin(async move { Ok(Svc(lua, remote_addr)) })
}
}
#[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);
}
}
+121
View File
@@ -0,0 +1,121 @@
use std::io;
use std::net::SocketAddr;
use std::rc::Rc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::task;
use mlua::{
chunk, AnyUserData, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods,
};
struct LuaTcpStream(TcpStream);
impl UserData for LuaTcpStream {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("peer_addr", |_, this, ()| {
Ok(this.0.peer_addr()?.to_string())
});
methods.add_async_function(
"read",
|lua, (this, size): (AnyUserData, usize)| async move {
let mut this = this.borrow_mut::<Self>()?;
let mut buf = vec![0; size];
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
},
);
methods.add_async_function(
"write",
|_, (this, data): (AnyUserData, LuaString)| async move {
let mut this = this.borrow_mut::<Self>()?;
let n = this.0.write(&data.as_bytes()).await?;
Ok(n)
},
);
methods.add_async_function("close", |_, this: AnyUserData| async move {
let mut this = this.borrow_mut::<Self>()?;
this.0.shutdown().await?;
Ok(())
});
}
}
async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let listener = TcpListener::bind(addr).await.expect("cannot bind addr");
println!("Listening on {}", addr);
let lua = Rc::new(lua);
let handler = Rc::new(handler);
loop {
let (stream, _) = match listener.accept().await {
Ok(res) => res,
Err(err) if is_transient_error(&err) => continue,
Err(err) => return Err(err),
};
let lua = lua.clone();
let handler = handler.clone();
task::spawn_local(async move {
let handler: Function = lua
.registry_value(&handler)
.expect("cannot get Lua handler");
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_fn = lua
.load(chunk! {
function(stream)
local peer_addr = stream:peer_addr()
print("connected from "..peer_addr)
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
stream:write("echo: "..data.."\n")
end
end
})
.eval::<Function>()
.expect("cannot create Lua handler");
// Store it in the Registry
let handler = lua
.create_registry_value(handler_fn)
.expect("cannot store Lua handler");
task::LocalSet::new()
.run_until(run_server(lua, handler))
.await
.expect("cannot run server")
}
fn is_transient_error(e: &io::Error) -> bool {
e.kind() == io::ErrorKind::ConnectionRefused
|| e.kind() == io::ErrorKind::ConnectionAborted
|| e.kind() == io::ErrorKind::ConnectionReset
}
+28 -18
View File
@@ -1,15 +1,15 @@
use std::f32;
use std::iter::FromIterator;
use mlua::{Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
fn main() -> Result<()> {
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
// *without* the debug library.
let lua = Lua::new();
// You can get and set global variables. Notice that the globals table here is a permanent
// reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is
// You can get and set global variables. Notice that the globals table here is a permanent
// reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is
// based heavily around sharing and internal mutation (just like Lua itself).
let globals = lua.globals();
@@ -20,8 +20,8 @@ fn main() -> Result<()> {
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
// 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
// messages are printed.
@@ -38,6 +38,17 @@ fn main() -> Result<()> {
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
assert_eq!(lua.load("return 1 + 2").eval::<i32>()?, 3);
// Use can use special `chunk!` macro to use Rust tokenizer and automatically capture variables
let a = 1;
let b = 2;
let name = "world";
lua.load(chunk! {
print($a + $b)
print("hello, " .. $name)
})
.exec()?;
// You can create and manage Lua tables
let array_table = lua.create_table()?;
@@ -76,7 +87,7 @@ fn main() -> Result<()> {
let print: Function = globals.get("print")?;
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 variadics using tuples. This is one way to call a function with
// multiple parameters:
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
@@ -87,15 +98,15 @@ fn main() -> Result<()> {
["hello", "yet", "again", "from", "rust"].iter().cloned(),
))?;
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
// first parameter, and the arguments given to the function as the second parameter. The type
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself 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.
let check_equal = lua.create_function(|_, (list1, list2): (Vec<String>, Vec<String>)| {
// This function just checks whether two string lists are equal, and in an inefficient way.
// Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return
// turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned.
// turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned.
Ok(list1 == list2)
})?;
globals.set("check_equal", check_equal)?;
@@ -164,18 +175,17 @@ fn main() -> Result<()> {
< f32::EPSILON
);
// Normally, Rust types passed to `Lua` must be `Send`, because `Lua` itself is `Send`, and
// 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 both of these requirements. 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 `Send` OR `'static`.
// 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`).
{
let mut rust_val = 0;
lua.scope(|scope| {
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
// because it could be unsafe.
lua.globals().set(
@@ -192,9 +202,9 @@ fn main() -> Result<()> {
assert_eq!(rust_val, 42);
}
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
// 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
// been invalidated and we will generate an error. If our function wasn't invalidated, we
// been invalidated and we will generate an error. If our function wasn't invalidated, we
// might be able to improperly access the freed `rust_val` which would be unsafe.
assert!(lua.load("sketchy()").exec().is_err());
+11
View File
@@ -0,0 +1,11 @@
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../..", features = ["module"] }
+39
View File
@@ -0,0 +1,39 @@
use mlua::prelude::*;
fn sum(_: &Lua, (a, b): (i64, i64)) -> LuaResult<i64> {
Ok(a + b)
}
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())
}
+66
View File
@@ -0,0 +1,66 @@
use mlua::{Error, Lua, LuaSerdeExt, Result, UserData, Value};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
enum Transmission {
Manual,
Automatic,
}
#[derive(Serialize, Deserialize)]
struct Engine {
v: u32,
kw: u32,
}
#[derive(Serialize, Deserialize)]
struct Car {
active: bool,
model: String,
transmission: Transmission,
engine: Engine,
}
impl UserData for Car {}
fn main() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
// Create Car struct from a Lua table
let car: Car = lua.from_value(lua.load(r#"
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
"#).eval()?)?;
// Set it as (serializable) userdata
globals.set("null", lua.null())?;
globals.set("array_mt", lua.array_metatable())?;
globals.set("car", lua.create_ser_userdata(car)?)?;
// Create a Lua table with multiple data types
let val: Value = lua
.load(r#"{driver = "Boris", car = car, price = null, points = setmetatable({}, array_mt)}"#)
.eval()?;
// Serialize the table above to JSON
let json_str = serde_json::to_string(&val).map_err(Error::external)?;
println!("{}", json_str);
// Create Lua Value from JSON (or any serializable type)
let json = serde_json::json!({
"key": "value",
"null": null,
"array": [],
});
globals.set("json_value", lua.to_value(&json)?)?;
lua.load(
r#"
assert(json_value["key"] == "value")
assert(json_value["null"] == null)
assert(#(json_value["array"]) == 0)
"#,
)
.exec()?;
Ok(())
}
+45
View File
@@ -0,0 +1,45 @@
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
#[derive(Default)]
struct Rectangle {
length: u32,
width: u32,
}
impl UserData for Rectangle {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, 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;
Ok(())
});
fields.add_field_method_get("width", |_, this| Ok(this.width));
fields.add_field_method_set("width", |_, this, val| {
this.width = val;
Ok(())
});
}
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, 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())
});
// Constructor
methods.add_meta_function(MetaMethod::Call, |_, ()| Ok(Rectangle::default()));
}
}
fn main() -> Result<()> {
let lua = Lua::new();
let rectangle = Rectangle::default();
lua.load(chunk! {
local rect = $rectangle()
rect.width = 10
rect.length = 5
assert(rect:area() == 50)
assert(rect:diagonal() - 11.1803 < 0.0001)
})
.exec()
}
+10 -3
View File
@@ -1,7 +1,7 @@
[package]
name = "mlua_derive"
version = "0.2.0"
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
version = "0.8.0-beta.1"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
description = "Procedural macros for the mlua crate."
repository = "https://github.com/khvzak/mlua"
@@ -11,7 +11,14 @@ license = "MIT"
[lib]
proc-macro = true
[features]
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
[dependencies]
proc-macro2 = "1.0"
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 }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.0", optional = true }
+105
View File
@@ -0,0 +1,105 @@
use proc_macro::{TokenStream, TokenTree};
use crate::token::{Pos, Token, Tokens};
#[derive(Debug, Clone)]
pub(crate) struct Capture {
key: Token,
rust: TokenTree,
}
impl Capture {
fn new(key: Token, rust: TokenTree) -> Self {
Self { key, rust }
}
/// Token string inside `chunk!`
pub(crate) fn key(&self) -> &Token {
&self.key
}
/// As rust variable, e.g. `x`
pub(crate) fn as_rust(&self) -> &TokenTree {
&self.rust
}
}
#[derive(Debug)]
pub(crate) struct Captures(Vec<Capture>);
impl Captures {
pub(crate) fn new() -> Self {
Self(Vec::new())
}
pub(crate) fn add(&mut self, token: &Token) -> Capture {
let tt = token.tree();
let key = token.clone();
match self.0.iter().find(|arg| arg.key() == &key) {
Some(arg) => arg.clone(),
None => {
let arg = Capture::new(key, tt.clone());
self.0.push(arg.clone());
arg
}
}
}
pub(crate) fn captures(&self) -> &[Capture] {
&self.0
}
}
#[derive(Debug)]
pub(crate) struct Chunk {
source: String,
caps: Captures,
}
impl Chunk {
pub(crate) fn new(tokens: TokenStream) -> Self {
let tokens = Tokens::retokenize(tokens);
let mut source = String::new();
let mut caps = Captures::new();
let mut pos: Option<Pos> = None;
for t in tokens {
if t.is_cap() {
caps.add(&t);
}
let (line, col) = (t.start().line, t.start().column);
let (prev_line, prev_col) = pos
.take()
.map(|lc| (lc.line, lc.column))
.unwrap_or_else(|| (line, col));
#[allow(clippy::comparison_chain)]
if line > prev_line {
source.push('\n');
} else if line == prev_line {
for _ in 0..col.saturating_sub(prev_col) {
source.push(' ');
}
}
source.push_str(&t.to_string());
pos = Some(t.end());
}
Self {
source: source.trim_end().to_string(),
caps,
}
}
pub(crate) fn source(&self) -> &str {
&self.source
}
pub(crate) fn captures(&self) -> &[Capture] {
self.caps.captures()
}
}
+103 -17
View File
@@ -1,35 +1,121 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote_spanned;
use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn};
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
#[cfg(feature = "macros")]
use {
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
proc_macro_error::proc_macro_error,
};
#[proc_macro_attribute]
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as AttributeArgs);
let item = parse_macro_input!(item as ItemFn);
let func = parse_macro_input!(item as ItemFn);
if args.len() > 0 {
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
if !args.is_empty() {
let err = Error::new(Span::call_site(), "the macro does not support arguments")
.to_compile_error();
return err.into();
}
let span = item.span();
let item_name = item.sig.ident.clone();
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site());
let func_name = func.sig.ident.clone();
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
let wrapped = quote! {
::mlua::require_module_feature!();
#func
let wrapped = quote_spanned! { span =>
#[no_mangle]
unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int {
#item
mlua::Lua::init_from_ptr(state)
.entrypoint1(#item_name)
.unwrap()
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")
}
};
wrapped.into()
}
#[cfg(feature = "macros")]
fn to_ident(tt: &TokenTree) -> TokenStream2 {
let s: TokenStream = tt.clone().into();
s.into()
}
#[cfg(feature = "macros")]
#[proc_macro]
#[proc_macro_error]
pub fn chunk(input: TokenStream) -> TokenStream {
let chunk = Chunk::new(input);
let source = chunk.source();
let caps_len = chunk.captures().len();
let caps = chunk.captures().iter().map(|cap| {
let cap_name = cap.as_rust().to_string();
let cap = to_ident(cap.as_rust());
quote! { env.raw_set(#cap_name, #cap)?; }
});
let wrapped_code = quote! {{
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
use ::std::borrow::Cow;
use ::std::io::Result as IoResult;
use ::std::marker::PhantomData;
use ::std::sync::Mutex;
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { 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>
where
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
{
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
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);
}
}
}
Ok(None)
}
fn mode(&self) -> Option<ChunkMode> {
Some(ChunkMode::Text)
}
}
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
let globals = lua.globals();
let env = lua.create_table()?;
let meta = lua.create_table()?;
meta.raw_set("__index", globals.clone())?;
meta.raw_set("__newindex", globals)?;
// Add captured variables
#(#caps)*
env.set_metatable(Some(meta));
Ok(Value::Table(env))
});
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
}};
wrapped_code.into()
}
#[cfg(feature = "macros")]
mod chunk;
#[cfg(feature = "macros")]
mod token;
+233
View File
@@ -0,0 +1,233 @@
use std::{
cmp::{Eq, PartialEq},
fmt::{self, Display, Formatter},
iter::IntoIterator,
vec::IntoIter,
};
use itertools::Itertools;
use once_cell::sync::Lazy;
use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
use proc_macro2::Span as Span2;
use regex::Regex;
#[derive(Clone, Copy, Debug)]
pub(crate) struct Pos {
pub(crate) line: usize,
pub(crate) column: usize,
}
impl Pos {
fn new(line: usize, column: usize) -> Self {
Self { line, column }
}
fn left(&self) -> Self {
Self {
line: self.line,
column: self.column.saturating_sub(1),
}
}
fn right(&self) -> Self {
Self {
line: self.line,
column: self.column.saturating_add(1),
}
}
}
fn span_pos(span: &Span) -> (Pos, Pos) {
let span2: Span2 = (*span).into();
let start = span2.start();
let end = span2.end();
// In stable, line/column information is not provided
// and set to 0 (line is 1-indexed)
if start.line == 0 || end.line == 0 {
return fallback_span_pos(span);
}
(
Pos::new(start.line, start.column),
Pos::new(end.line, end.column),
)
}
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
// Workaround to somehow retrieve location information in span in stable rust :(
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
match RE.captures(&format!("{:?}", span)) {
Some(caps) => match (caps.get(1), caps.get(2)) {
(Some(start), Some(end)) => Some((
match start.as_str().parse() {
Ok(v) => v,
_ => return None,
},
match end.as_str().parse() {
Ok(v) => v,
_ => return None,
},
)),
_ => None,
},
None => None,
}
}
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"
),
};
(Pos::new(1, start), Pos::new(1, end))
}
/// Attribute of token.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum TokenAttr {
/// No attribute
None,
/// Starts with `$`
Cap,
}
#[derive(Clone, Debug)]
pub(crate) struct Token {
source: String,
tree: TokenTree,
start: Pos,
end: Pos,
attr: TokenAttr,
}
impl PartialEq for Token {
fn eq(&self, other: &Self) -> bool {
self.source == other.source && self.attr == other.attr
}
}
impl Eq for Token {}
impl Token {
fn new(tree: TokenTree) -> Self {
let (start, end) = span_pos(&tree.span());
Self {
source: tree.to_string(),
start,
end,
tree,
attr: TokenAttr::None,
}
}
fn new_delim(source: String, tree: TokenTree, open: bool) -> Self {
let (start, end) = span_pos(&tree.span());
let (start, end) = if open {
(start, start.right())
} else {
(end.left(), end)
};
Self {
source,
tree,
start,
end,
attr: TokenAttr::None,
}
}
pub(crate) fn tree(&self) -> &TokenTree {
&self.tree
}
pub(crate) fn is_cap(&self) -> bool {
self.attr == TokenAttr::Cap
}
pub(crate) fn start(&self) -> Pos {
self.start
}
pub(crate) fn end(&self) -> Pos {
self.end
}
fn is(&self, s: &str) -> bool {
self.source == s
}
fn attr(mut self, attr: TokenAttr) -> Self {
self.attr = attr;
self
}
}
#[derive(Debug)]
pub(crate) struct Tokens(pub(crate) Vec<Token>);
impl Tokens {
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
Tokens(
tt.into_iter()
.flat_map(Tokens::from)
.peekable()
.batching(|iter| {
// Find variable tokens
let t = iter.next()?;
if t.is("$") {
// `$` + `ident` => `$ident`
let t = iter.next().expect("$ must trail an identifier");
Some(t.attr(TokenAttr::Cap))
} else {
Some(t)
}
})
.collect(),
)
}
}
impl IntoIterator for Tokens {
type Item = Token;
type IntoIter = IntoIter<Token>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<TokenTree> for Tokens {
fn from(tt: TokenTree) -> Self {
let tts = match tt.clone() {
TokenTree::Group(g) => {
let (b, e) = match g.delimiter() {
Delimiter::Parenthesis => ("(", ")"),
Delimiter::Brace => ("{", "}"),
Delimiter::Bracket => ("[", "]"),
Delimiter::None => ("", ""),
};
let (b, e) = (b.into(), e.into());
vec![Token::new_delim(b, tt.clone(), true)]
.into_iter()
.chain(g.stream().into_iter().flat_map(Tokens::from))
.chain(vec![Token::new_delim(e, tt, false)])
.collect()
}
_ => vec![Token::new(tt)],
};
Tokens(tts)
}
}
impl Display for Token {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.source)
}
}
+462
View File
@@ -0,0 +1,462 @@
use std::borrow::Cow;
use std::ffi::CString;
use std::io::Result as IoResult;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::value::{FromLuaMulti, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// 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
/// [`Chunk`]: crate::Chunk
pub trait AsChunk<'lua> {
/// Returns chunk data (can be text or binary)
fn source(&self) -> IoResult<Cow<[u8]>>;
/// Returns optional chunk name
fn name(&self) -> Option<StdString> {
None
}
/// Returns optional chunk [environment]
///
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
fn env(&self, _lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
Ok(None)
}
/// Returns optional chunk mode (text or binary)
fn mode(&self) -> Option<ChunkMode> {
None
}
}
impl<'lua> AsChunk<'lua> for str {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl<'lua> AsChunk<'lua> for StdString {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl<'lua> AsChunk<'lua> for [u8] {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl<'lua> AsChunk<'lua> for Vec<u8> {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl<'lua> AsChunk<'lua> for Path {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
}
impl<'lua> AsChunk<'lua> for PathBuf {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
}
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
///
/// [`Lua::load`]: crate::Lua::load
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
pub(crate) lua: &'lua Lua,
pub(crate) source: IoResult<Cow<'a, [u8]>>,
pub(crate) name: Option<StdString>,
pub(crate) env: Result<Option<Value<'lua>>>,
pub(crate) mode: Option<ChunkMode>,
#[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,
coverage_level: u8,
vector_lib: Option<String>,
vector_ctor: Option<String>,
mutable_globals: Vec<String>,
}
#[cfg(any(feature = "luau", doc))]
impl Default for Compiler {
fn default() -> Self {
// Defaults are taken from luacode.h
Compiler {
optimization_level: 1,
debug_level: 1,
coverage_level: 0,
vector_lib: None,
vector_ctor: None,
mutable_globals: Vec::new(),
}
}
}
#[cfg(any(feature = "luau", doc))]
impl Compiler {
/// Creates Luau compiler instance with default options
pub fn new() -> Self {
Compiler::default()
}
/// 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
pub 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
pub fn set_debug_level(mut self, level: u8) -> Self {
self.debug_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)
pub fn set_coverage_level(mut self, level: u8) -> Self {
self.coverage_level = level;
self
}
#[doc(hidden)]
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
self.vector_lib = lib;
self
}
#[doc(hidden)]
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
self.vector_ctor = ctor;
self
}
/// Sets a list of globals that are mutable.
///
/// It disables the import optimization for fields accessed through these.
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
self.mutable_globals = globals;
self
}
/// Compiles the `source` into bytecode.
pub fn compile(&self, source: impl AsRef<[u8]>) -> 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 mutable_globals = self
.mutable_globals
.iter()
.map(|name| CString::new(name.clone()).ok())
.collect::<Option<Vec<_>>>()
.unwrap_or_default();
let mut mutable_globals = mutable_globals
.iter()
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let mut mutable_globals_ptr = ptr::null_mut();
if !mutable_globals.is_empty() {
mutable_globals.push(ptr::null());
mutable_globals_ptr = mutable_globals.as_mut_ptr();
}
unsafe {
let options = ffi::lua_CompileOptions {
optimizationLevel: self.optimization_level as c_int,
debugLevel: self.debug_level as c_int,
coverageLevel: self.coverage_level as c_int,
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
mutableGlobals: mutable_globals_ptr,
};
ffi::luau_compile(source.as_ref(), options)
}
}
}
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
self.name = Some(name.as_ref().to_string());
// Do extra validation
let _ = self.convert_name()?;
Ok(self)
}
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
///
/// Lua 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<V: ToLua<'lua>>(mut self, env: V) -> Result<Self> {
// Prefer to propagate errors here and wrap to `Ok`
self.env = Ok(Some(env.to_lua(self.lua)?));
Ok(self)
}
/// 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(())?;
Ok(())
}
/// Asynchronously execute this chunk of code.
///
/// See [`exec`] for more details.
///
/// Requires `feature = "async"`
///
/// [`exec`]: #method.exec
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
where
'lua: 'fut,
{
self.call_async(())
}
/// 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<'lua>>(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`]: #method.eval
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
R: FromLuaMulti<'lua> + 'fut,
{
if self.detect_mode() == ChunkMode::Binary {
self.call_async(())
} else if let Ok(function) = self.to_expression() {
function.call_async(())
} else {
self.call_async(())
}
}
/// 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<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> 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`]: #method.call
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
match self.into_function() {
Ok(func) => func.call_async(args),
Err(e) => Box::pin(future::err(e)),
}
}
/// 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<'lua>> {
#[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.lua
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
}
/// Compiles the chunk and changes mode to binary.
///
/// It does nothing if the chunk is already binary.
#[cfg(feature = "luau")]
fn compile(&mut self) {
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
let data = self
.compiler
.get_or_insert_with(Default::default)
.compile(source);
self.mode = Some(ChunkMode::Binary);
self.source = Ok(Cow::Owned(data));
}
}
}
fn to_expression(&self) -> Result<Function<'lua>> {
// We assume that mode is Text
let source = self.source.as_ref();
let source = source.map_err(|err| Error::RuntimeError(err.to_string()))?;
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))
.unwrap_or(source);
let name = self.convert_name()?;
self.lua
.load_chunk(&source, name.as_deref(), self.env.clone()?, None)
}
fn detect_mode(&self) -> ChunkMode {
match (self.mode, &self.source) {
(Some(mode), _) => mode,
(None, Ok(source)) if source.len() == 0 => ChunkMode::Text,
(None, Ok(source)) => {
#[cfg(not(feature = "luau"))]
if source.starts_with(ffi::LUA_SIGNATURE) {
return ChunkMode::Binary;
}
#[cfg(feature = "luau")]
if source[0] < b'\n' {
return ChunkMode::Binary;
}
ChunkMode::Text
}
(None, Err(_)) => ChunkMode::Text, // any value is fine
}
}
fn convert_name(&self) -> Result<Option<CString>> {
self.name
.clone()
.map(CString::new)
.transpose()
.map_err(|err| Error::RuntimeError(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
}
}
+219 -28
View File
@@ -1,4 +1,8 @@
use std::collections::{BTreeMap, HashMap};
#![allow(clippy::wrong_self_convention)]
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::convert::TryInto;
use std::ffi::{CStr, CString};
use std::hash::{BuildHasher, Hash};
use std::string::String as StdString;
@@ -12,29 +16,33 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{LightUserData, Number};
use crate::types::{LightUserData, MaybeSend};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
impl<'lua> ToLua<'lua> for Value<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(self)
}
}
impl<'lua> FromLua<'lua> for Value<'lua> {
#[inline]
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(lua_value)
}
}
impl<'lua> ToLua<'lua> for String<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(self))
}
}
impl<'lua> FromLua<'lua> for String<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
let ty = value.type_name();
lua.coerce_string(value)?
@@ -47,12 +55,14 @@ impl<'lua> FromLua<'lua> for String<'lua> {
}
impl<'lua> ToLua<'lua> for Table<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(self))
}
}
impl<'lua> FromLua<'lua> for Table<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
match value {
Value::Table(table) => Ok(table),
@@ -66,12 +76,14 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
}
impl<'lua> ToLua<'lua> for Function<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(self))
}
}
impl<'lua> FromLua<'lua> for Function<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
match value {
Value::Function(table) => Ok(table),
@@ -85,12 +97,14 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
}
impl<'lua> ToLua<'lua> for Thread<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Thread(self))
}
}
impl<'lua> FromLua<'lua> for Thread<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
match value {
Value::Thread(t) => Ok(t),
@@ -104,12 +118,14 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
}
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(self))
}
}
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
match value {
Value::UserData(ud) => Ok(ud),
@@ -122,13 +138,15 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
}
impl<'lua, T: 'static + Send + UserData> ToLua<'lua> for T {
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?))
}
}
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
@@ -142,12 +160,14 @@ impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
}
impl<'lua> ToLua<'lua> for Error {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Error(self))
}
}
impl<'lua> FromLua<'lua> for Error {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
match value {
Value::Error(err) => Ok(err),
@@ -161,13 +181,15 @@ impl<'lua> FromLua<'lua> for Error {
}
impl<'lua> ToLua<'lua> for bool {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self))
}
}
impl<'lua> FromLua<'lua> for bool {
fn from_lua(v: Value, _: &'lua Lua) -> Result<Self> {
#[inline]
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match v {
Value::Nil => Ok(false),
Value::Boolean(b) => Ok(b),
@@ -177,13 +199,15 @@ impl<'lua> FromLua<'lua> for bool {
}
impl<'lua> ToLua<'lua> for LightUserData {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::LightUserData(self))
}
}
impl<'lua> FromLua<'lua> for LightUserData {
fn from_lua(value: Value, _: &'lua Lua) -> Result<Self> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::LightUserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
@@ -196,12 +220,14 @@ impl<'lua> FromLua<'lua> for LightUserData {
}
impl<'lua> ToLua<'lua> for StdString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
impl<'lua> FromLua<'lua> for StdString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(lua
@@ -216,12 +242,41 @@ impl<'lua> FromLua<'lua> for StdString {
}
}
impl<'lua, 'a> ToLua<'lua> for &'a str {
impl<'lua> ToLua<'lua> for &str {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
}
impl<'lua> ToLua<'lua> for Cow<'_, str> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> ToLua<'lua> for Box<str> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&*self)?))
}
}
impl<'lua> FromLua<'lua> for Box<str> {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(lua
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "Box<str>",
message: Some("expected string or number".to_string()),
})?
.to_str()?
.to_owned()
.into_boxed_str())
}
}
impl<'lua> ToLua<'lua> for CString {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
@@ -250,13 +305,19 @@ impl<'lua> FromLua<'lua> for CString {
}
}
impl<'lua, 'a> ToLua<'lua> for &'a CStr {
impl<'lua> ToLua<'lua> for &CStr {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua, 'a> ToLua<'lua> for BString {
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> ToLua<'lua> for BString {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
@@ -278,7 +339,7 @@ impl<'lua> FromLua<'lua> for BString {
}
}
impl<'lua, 'a> ToLua<'lua> for &BStr {
impl<'lua> ToLua<'lua> for &BStr {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
@@ -288,24 +349,24 @@ macro_rules! lua_convert_int {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
if let Some(i) = cast(self) {
Ok(Value::Integer(i))
} else {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
to: "number",
message: Some("out of range".to_owned()),
})
.map(Value::Number)
}
cast(self)
.map(Value::Integer)
.or_else(|| cast(self).map(Value::Number))
// This is impossible error because conversion to Number never fails
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
to: "number",
message: Some("out of range".to_owned()),
})
}
}
impl<'lua> FromLua<'lua> for $x {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
(if let Some(i) = lua.coerce_integer(value.clone())? {
(if let Value::Integer(i) = value {
cast(i)
} else if let Some(i) = lua.coerce_integer(value.clone())? {
cast(i)
} else {
cast(lua.coerce_number(value)?.ok_or_else(|| {
@@ -345,7 +406,13 @@ macro_rules! lua_convert_float {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Number(self as Number))
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
to: "number",
message: Some("out of range".to_string()),
})
.map(Value::Number)
}
}
@@ -373,6 +440,73 @@ macro_rules! lua_convert_float {
lua_convert_float!(f32);
lua_convert_float!(f64);
impl<'lua, T> ToLua<'lua> for &[T]
where
T: Clone + ToLua<'lua>,
{
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
))
}
}
impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
where
T: ToLua<'lua>,
{
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
where
T: FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!(
vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]
.try_into()
.map_err(|_| ()),
"cannot convert vector to array"
)),
Value::Table(table) => {
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
vec.try_into()
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
from: "Table",
to: "Array",
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
})
}
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Array",
message: Some("expected table".to_string()),
}),
}
}
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
}
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
}
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
@@ -380,15 +514,20 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
if let Value::Table(table) = value {
table.sequence_values().collect()
} else {
Err(Error::FromLuaConversionError {
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => Ok(vec![
T::from_lua(Value::Number(x as _), _lua)?,
T::from_lua(Value::Number(y as _), _lua)?,
T::from_lua(Value::Number(z as _), _lua)?,
]),
Value::Table(table) => table.sequence_values().collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Vec",
message: Some("expected table".to_string()),
})
}),
}
}
}
@@ -437,7 +576,58 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
}
}
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
)?))
}
}
impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua> for HashSet<T, S> {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
.collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "HashSet",
message: Some("expected table".to_string()),
}),
}
}
}
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
)?))
}
}
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
.collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "BTreeSet",
message: Some("expected table".to_string()),
}),
}
}
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
match self {
Some(val) => val.to_lua(lua),
@@ -447,6 +637,7 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
match value {
Nil => Ok(None),
+158 -30
View File
@@ -1,11 +1,17 @@
#![allow(clippy::wrong_self_convention)]
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;
use std::net::AddrParseError;
use std::result::Result as StdResult;
use std::str::Utf8Error;
use std::string::String as StdString;
use std::sync::Arc;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Error {
/// Syntax error while parsing Lua source code.
SyntaxError {
@@ -31,8 +37,21 @@ 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.
///
/// 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,
/// 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.
@@ -72,14 +91,14 @@ pub enum Error {
},
/// [`Thread::resume`] was called on an inactive coroutine.
///
/// A coroutine is inactive if its main function has returned or if an error has occured inside
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
/// the coroutine.
///
/// [`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
/// [`Thread::resume`]: crate::Thread::resume
/// [`Thread::status`]: crate::Thread::status
CoroutineInactive,
/// An [`AnyUserData`] is not the expected type in a borrow.
///
@@ -87,17 +106,24 @@ pub enum Error {
/// 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`]: crate::AnyUserData
UserDataDestructed,
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
///
/// 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.
///
@@ -105,10 +131,24 @@ pub enum Error {
/// 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 `RegistryKey` produced from a different Lua state was used.
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
///
/// [`MetaMethod`]: crate::MetaMethod
MetaMethodRestricted(StdString),
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
///
/// [`MetaMethod`]: crate::MetaMethod
MetaMethodTypeError {
method: StdString,
type_name: &'static str,
message: Option<StdString>,
},
/// 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 {
@@ -117,6 +157,19 @@ pub enum Error {
/// Original error returned by the Rust code.
cause: Arc<Error>,
},
/// A Rust panic that was previously resumed, returned again.
///
/// This error can occur only when a Rust panic resumed previously was recovered
/// and returned again.
PreviouslyResumedPanic,
/// Serialization error.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
SerializeError(StdString),
/// Deserialization error.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
DeserializeError(StdString),
/// A custom error.
///
/// This can be used for returning user-defined errors from callbacks.
@@ -130,6 +183,7 @@ pub enum Error {
/// A specialized `Result` type used by `mlua`'s API.
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 {
@@ -142,6 +196,15 @@ impl fmt::Display for Error {
Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {}", msg)
}
Error::SafetyError(ref 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::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
Error::CallbackDestructed => write!(
fmt,
@@ -155,22 +218,14 @@ impl fmt::Display for Error {
fmt,
"too many arguments to Function::bind"
),
Error::ToLuaConversionError {
from,
to,
ref message,
} => {
Error::ToLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting {} to Lua {}", from, to)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::FromLuaConversionError {
from,
to,
ref message,
} => {
Error::FromLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting Lua {} to {}", from, to)?;
match *message {
None => Ok(()),
@@ -179,15 +234,55 @@ impl fmt::Display for Error {
}
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
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 {
None => Ok(()),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref traceback, ref cause } => {
write!(fmt, "callback error: {}: {}", cause, traceback)
Error::CallbackError { ref cause, ref traceback } => {
writeln!(fmt, "callback error")?;
// Trace errors down to the root
let (mut cause, mut full_traceback) = (cause, None);
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
cause = cause2;
full_traceback = Some(traceback2);
}
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())?;
}
write!(fmt, "caused by: {}", cause)
}
Error::ExternalError(ref err) => write!(fmt, "external error: {}", err),
Error::PreviouslyResumedPanic => {
write!(fmt, "previously resumed panic returned again")
}
#[cfg(feature = "serialize")]
Error::SerializeError(ref err) => {
write!(fmt, "serialize error: {}", err)
},
#[cfg(feature = "serialize")]
Error::DeserializeError(ref err) => {
write!(fmt, "deserialize error: {}", err)
},
Error::ExternalError(ref err) => write!(fmt, "{}", err),
}
}
}
@@ -195,8 +290,12 @@ impl fmt::Display for Error {
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) => Some(err.as_ref()),
// 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(ref err) => err.source(),
_ => None,
}
}
@@ -212,10 +311,7 @@ pub trait ExternalError {
fn to_lua_err(self) -> Error;
}
impl<E> ExternalError for E
where
E: Into<Box<dyn StdError + Send + Sync>>,
{
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
fn to_lua_err(self) -> Error {
Error::external(self)
}
@@ -233,3 +329,35 @@ where
self.map_err(|e| e.to_lua_err())
}
}
impl std::convert::From<AddrParseError> for Error {
fn from(err: AddrParseError) -> Self {
Error::external(err)
}
}
impl std::convert::From<IoError> for Error {
fn from(err: IoError) -> Self {
Error::external(err)
}
}
impl std::convert::From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::external(err)
}
}
#[cfg(feature = "serialize")]
impl serde::ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self::SerializeError(msg.to_string())
}
}
#[cfg(feature = "serialize")]
impl serde::de::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self::DeserializeError(msg.to_string())
}
}
-798
View File
@@ -1,798 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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
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_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_pushnil, 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 != ptr::null() && strchr(mode, *modename) == ptr::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"))]
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: &'static 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) {
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"))]
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);
}
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 i as f64 == n {
return 1;
}
}
return 0;
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
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 != ptr::null_mut() {
*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
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);
if ok != 0 {
if n == n as lua_Integer as lua_Number {
if isnum != ptr::null_mut() {
*isnum = 1;
}
return n as lua_Integer;
}
}
if isnum != ptr::null_mut() {
*isnum = 0;
}
return 0;
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
lua_objlen(L, idx)
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
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"))]
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")]
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"))]
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")]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getglobal_old(L, var);
lua_type(L, -1)
}
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
lua_gettable_old(L, idx);
lua_type(L, -1)
}
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)
}
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
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
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"))]
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")]
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"))]
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")]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getuservalue_old(L, idx);
lua_type(L, -1)
}
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"))]
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"))]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
luaL_checktype(L, -1, lua::LUA_TTABLE);
lua_setfenv(L, idx);
}
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"))]
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"))]
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)),
);
}
}
}
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
}
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 == ptr::null_mut() {
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 != ptr::null_mut() {
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"))]
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 != ptr::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
}
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
}
}
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"))]
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"))]
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 = std::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 != ptr::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:"), cstr!("ok") /*ar.short_src*/);
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"))]
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"))]
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 == ptr::null_mut() || 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"))]
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 != ptr::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);
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);
}
-285
View File
@@ -1,285 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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";
}
}
const char *rs_uint_type(int width) {
switch (width) {
default:
case 2:
return "u16";
case 4:
return "u32";
case 8:
return "u64";
}
}
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: &'static 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_STR("LUA_PATH_DEFAULT", LUA_PATH_DEFAULT),
RS_STR("LUA_CPATH_DEFAULT", LUA_CPATH_DEFAULT),
RS_STR("LUA_DIRSEP", LUA_DIRSEP),
RS_INT("LUA_EXTRASPACE", LUA_EXTRASPACE),
RS_INT("LUA_IDSIZE", LUA_IDSIZE),
RS_INT("LUAL_BUFFERSIZE", LUAL_BUFFERSIZE),
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_STR("LUA_VERSION", LUA_VERSION),
RS_STR("LUA_RELEASE", LUA_RELEASE),
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),
#if LUA_VERSION_NUM == 503
RS_STR("LUA_UTF8LIBNAME", LUA_UTF8LIBNAME),
#endif
#if LUA_VERSION_NUM >= 502
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),
};
if (!write_items(f, glue_entries)) {
printf("%s: error generating %s; aborting\n", argv[0], filename);
return EXIT_FAILURE;
}
fclose(f);
return EXIT_SUCCESS;
}
-304
View File
@@ -1,304 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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(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 = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn luaL_checkversion(L: *mut lua_State) {
#[cfg(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(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(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(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 = "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(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 = "lua53", feature = "lua52"))]
pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
#[cfg(any(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 = "lua53", feature = "lua52"))]
pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
#[cfg(any(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 = "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 = "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 = "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 = "lua53", feature = "lua52"))]
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;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;
#[cfg(any(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(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 = "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
+580
View File
@@ -0,0 +1,580 @@
//! MLua compatibility layer for Lua 5.1/JIT
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::convert::TryInto;
use std::mem;
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;
}
}
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, ".");
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, "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,
);
}
}
//
// 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"));
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"));
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, "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
}
}
#[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"));
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, "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_TNIL => {
lua_pushliteral(L, "nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
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_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);
}
}
};
} 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"));
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"));
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);
#[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 cfg!(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);
}
+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;
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
extern "C" {
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, ...) -> !;
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;
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#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
//
+380
View File
@@ -0,0 +1,380 @@
//! Contains definitions from `lua.h`.
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`
pub type lua_Integer = isize;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> 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;
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(all(feature = "luajit", feature = "vendored"))]
pub fn lua_resetthread(L: *mut lua_State, th: *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;
extern "C" {
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);
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);
}
//
// 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 str) {
use std::ffi::CString;
let c_str = CString::new(s).unwrap();
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
}
#[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_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 = 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" 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;
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,
}
+42
View File
@@ -0,0 +1,42 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_IOLIBNAME: &str = "io";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
#[cfg(feature = "luajit")]
pub const LUA_BITLIBNAME: &str = "bit";
#[cfg(feature = "luajit")]
pub const LUA_JITLIBNAME: &str = "jit";
#[cfg(feature = "luajit")]
pub const LUA_FFILIBNAME: &str = "ffi";
extern "C" {
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;
+265
View File
@@ -0,0 +1,265 @@
//! MLua compatibility layer for Lua 5.2
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::convert::TryInto;
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, 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_TNIL => {
lua_pushliteral(L, "nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
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_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);
}
}
};
} 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, 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);
}
lua_replace(L, -2);
}
+184
View File
@@ -0,0 +1,184 @@
//! 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;
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
extern "C" {
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, ...) -> !;
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;
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#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())
}
extern "C" {
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
//
+467
View File
@@ -0,0 +1,467 @@
//! Contains definitions from `lua.h`.
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`
pub type lua_Integer = isize;
/// 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" fn(L: *mut lua_State) -> 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;
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;
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;
extern "C" {
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" {
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;
}
extern "C" {
//
// 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)
}
extern "C" {
//
// 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;
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
extern "C" {
//
// Miscellaneous functions
//
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);
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);
}
//
// 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 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())
}
#[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_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 = 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" 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;
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,
}
+31
View File
@@ -0,0 +1,31 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_IOLIBNAME: &str = "io";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_BITLIBNAME: &str = "bit32";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
extern "C" {
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;
+19
View File
@@ -0,0 +1,19 @@
//! MLua compatibility layer for Lua 5.2
use std::os::raw::c_int;
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
}
+184
View File
@@ -0,0 +1,184 @@
//! 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: &str = "_LOADED";
// Key, in the registry, for table of preloaded loaders
pub const LUA_PRELOAD_TABLE: &str = "_PRELOAD";
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
extern "C" {
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, ...) -> !;
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;
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#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())
}
extern "C" {
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);
}
// 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
//
+91 -303
View File
@@ -1,84 +1,52 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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 = "lua53", feature = "lua52"))]
use std::os::raw::c_uchar;
use std::os::raw::{c_char, c_int, c_void};
use std::marker::{PhantomData, PhantomPinned};
use std::mem;
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_void};
use std::ptr;
use super::luaconf;
// Mark for precompiled code (`<esc>Lua`)
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
pub use super::glue::{LUA_RELEASE, LUA_VERSION, LUA_VERSION_NUM};
pub use super::glue::LUA_REGISTRYINDEX;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
pub const LUA_SIGNATURE: &'static [u8] = b"\x1bLua";
// option for multiple returns in 'lua_pcall' and 'lua_call'
// 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,
};
// Size of the Lua stack
pub const LUAI_MAXSTACK: c_int = 1000000;
#[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, lua_setuservalue, lua_tonumberx, lua_upvalueindex,
};
// Size of a raw memory area associated with a Lua state with very fast access.
pub const LUA_EXTRASPACE: usize = mem::size_of::<*const ()>();
#[cfg(feature = "lua52")]
pub use super::compat53::lua_getglobal;
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXSTACK - 1000;
#[cfg(any(feature = "lua53", feature = "lua52"))]
#[inline(always)]
pub fn lua_upvalueindex(i: c_int) -> c_int {
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_REGISTRYINDEX - i
}
// thread status
//
// 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 = "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;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
// basic types
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
@@ -91,38 +59,32 @@ pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_NUMTAGS: c_int = 9;
// minimum stack available to a C function
/// Minimum Lua stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
// predefined values in the registry
#[cfg(any(feature = "lua53", feature = "lua52"))]
// Predefined values in the registry
pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
/// A Lua number, usually equivalent to `f64`.
pub type lua_Number = luaconf::LUA_NUMBER;
/// A Lua number, usually equivalent to `f64`
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`.
pub type lua_Integer = luaconf::LUA_INTEGER;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = i64;
// unsigned integer type
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
/// A Lua unsigned integer, usually equivalent to `u64`
pub type lua_Unsigned = u64;
// type for continuation-function contexts
#[cfg(feature = "lua53")]
pub type lua_KContext = luaconf::LUA_KCONTEXT;
/// Type for continuation-function contexts
pub type lua_KContext = isize;
/// Type for native functions that can be passed to Lua.
/// Type for native C 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(feature = "lua53")]
/// Type for continuation functions
pub type lua_KFunction =
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
@@ -132,7 +94,7 @@ pub type lua_Reader =
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.
/// Type for memory-allocation functions
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
@@ -141,105 +103,71 @@ pub type lua_Alloc = unsafe extern "C" fn(
) -> *mut c_void;
extern "C" {
// state manipulation
//
// 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;
#[cfg(feature = "lua53")]
pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
// basic stack manipulation
#[cfg(any(feature = "lua53", feature = "lua52"))]
//
// 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);
#[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(feature = "lua53")]
pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int);
#[cfg(any(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)
//
// 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(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 = "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(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 = "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_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;
#[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(feature = "lua53")]
pub const LUA_OPMOD: c_int = 3;
#[cfg(feature = "lua53")]
pub const LUA_OPPOW: c_int = 4;
#[cfg(feature = "lua53")]
pub const LUA_OPDIV: c_int = 5;
#[cfg(feature = "lua53")]
pub const LUA_OPIDIV: c_int = 6;
#[cfg(feature = "lua53")]
pub const LUA_OPBAND: c_int = 7;
#[cfg(feature = "lua53")]
pub const LUA_OPBOR: c_int = 8;
#[cfg(feature = "lua53")]
pub const LUA_OPBXOR: c_int = 9;
#[cfg(feature = "lua53")]
pub const LUA_OPSHL: c_int = 10;
#[cfg(feature = "lua53")]
pub const LUA_OPSHR: c_int = 11;
#[cfg(feature = "lua53")]
pub const LUA_OPUNM: c_int = 12;
#[cfg(feature = "lua53")]
pub const LUA_OPBNOT: c_int = 13;
extern "C" {
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn lua_arith(L: *mut lua_State, op: c_int);
}
@@ -248,118 +176,58 @@ 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 = "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" {
//
// 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);
#[cfg(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 = "lua53", feature = "lua52"))]
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;
#[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(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(feature = "lua53")]
//
// 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;
#[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(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(feature = "lua53")]
pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int;
#[cfg(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(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(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);
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 = "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);
}
// set functions (stack -> Lua)
extern "C" {
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub fn lua_setglobal(L: *mut lua_State, var: *const c_char);
//
// 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);
#[cfg(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 = "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(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;
}
// 'load' and 'call' functions (load and run Lua code)
extern "C" {
#[cfg(feature = "lua53")]
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
@@ -367,16 +235,6 @@ extern "C" {
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(feature = "lua53")]
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
@@ -385,94 +243,57 @@ extern "C" {
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,
data: *mut c_void,
chunkname: *const c_char,
mode: *const c_char,
) -> c_int;
#[cfg(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 = "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 = "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(feature = "lua53")]
//
// Coroutine functions
//
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(any(feature = "lua53", feature = "lua52"))]
pub fn lua_resume(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_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
#[cfg(feature = "lua53")]
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
}
#[cfg(any(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)
}
// garbage-collection function and options
//
// 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;
@@ -481,41 +302,38 @@ 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 = "lua53", feature = "lua52"))]
pub const LUA_GCISRUNNING: c_int = 9;
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
// miscellaneous functions
extern "C" {
//
// Miscellaneous functions
//
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 = "lua53", feature = "lua52"))]
pub fn lua_len(L: *mut lua_State, idx: c_int);
#[cfg(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);
}
// some useful macros
// here, implemented as Rust functions
#[cfg(feature = "lua53")]
//
// Some useful macros (implemented as Rust functions)
//
#[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
(L as *mut c_char).sub(LUA_EXTRASPACE) as *mut c_void
}
#[cfg(any(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 = "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())
@@ -589,19 +407,6 @@ pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_ch
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 = "lua53", feature = "lua52"))]
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
@@ -612,27 +417,30 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[cfg(feature = "lua53")]
#[inline(always)]
pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) {
lua_rotate(L, idx, 1)
}
#[cfg(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(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
//
// 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;
@@ -647,7 +455,7 @@ 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 = extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
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;
@@ -657,18 +465,15 @@ extern "C" {
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 = "lua53", feature = "lua52"))]
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
#[cfg(any(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: lua_Hook, mask: c_int, count: 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(any(feature = "lua53", feature = "lua52"))]
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
@@ -683,24 +488,7 @@ pub struct lua_Debug {
pub nparams: c_uchar,
pub isvararg: c_char,
pub istailcall: c_char,
pub short_src: [c_char; luaconf::LUA_IDSIZE as usize],
pub short_src: [c_char; LUA_IDSIZE],
// 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,
}
+33
View File
@@ -0,0 +1,33 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_IOLIBNAME: &str = "io";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_BITLIBNAME: &str = "bit32";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
extern "C" {
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;
+186
View File
@@ -0,0 +1,186 @@
//! 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: &str = "_LOADED";
// Key, in the registry, for table of preloaded loaders
pub const LUA_PRELOAD_TABLE: &str = "_PRELOAD";
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
extern "C" {
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, ...) -> !;
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;
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#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())
}
extern "C" {
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())
}
//
// TODO: Generic Buffer Manipulation
//
+533
View File
@@ -0,0 +1,533 @@
//! Contains definitions from `lua.h`.
use std::marker::{PhantomData, PhantomPinned};
use std::mem;
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, 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;
// 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" fn(L: *mut lua_State) -> c_int;
/// Type for continuation functions
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
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;
pub fn lua_resetthread(L: *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;
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;
extern "C" {
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" {
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;
}
extern "C" {
//
// 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)
}
extern "C" {
//
// 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
//
extern "C" {
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;
extern "C" {
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
}
extern "C" {
//
// Miscellaneous functions
//
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);
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);
}
//
// 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 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())
}
#[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())
}
#[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_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" 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;
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,
}
+31
View File
@@ -0,0 +1,31 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_IOLIBNAME: &str = "io";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_LOADLIBNAME: &str = "package";
extern "C" {
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;
-37
View File
@@ -1,37 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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::LUAL_BUFFERSIZE;
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(feature = "lua53")]
pub use super::glue::LUAL_NUMSIZES;
#[cfg(feature = "lua53")]
pub type LUA_KCONTEXT = isize; // intptr_t
-58
View File
@@ -1,58 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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 = "lua53", feature = "lua52"))]
pub use super::glue::LUA_BITLIBNAME;
#[cfg(feature = "lua53")]
pub use super::glue::LUA_UTF8LIBNAME;
extern "C" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
#[cfg(any(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(feature = "lua53")]
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
#[cfg(any(feature = "lua53", 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;
pub fn luaL_openlibs(L: *mut lua_State);
}
+518
View File
@@ -0,0 +1,518 @@
//! MLua compatibility layer for Roblox Luau.
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::ffi::CStr;
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lauxlib::*;
use super::lua::*;
use super::luacode::*;
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, ".");
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, "?");
}
}
//
// 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"));
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())
}
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 {
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, "__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) {
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"));
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, "__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, "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
}
//
// 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, "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_loadbufferx(
L: *mut lua_State,
data: *const c_char,
mut size: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int {
extern "C" {
fn free(p: *mut c_void);
}
let chunk_is_text = (*data as u8) >= b'\n';
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 {
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
let ok = luau_load(L, name, data, size, 0) == 0;
free(data as *mut c_void);
if !ok {
return LUA_ERRSYNTAX;
}
} else if luau_load(L, name, data, size, 0) != 0 {
return LUA_ERRSYNTAX;
}
LUA_OK
}
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
) -> c_int {
luaL_loadbufferx(L, data, size, name, ptr::null())
}
#[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_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, "stack traceback:");
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
if level + 1 == mark {
// too many levels?
lua_pushliteral(L, "\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.as_ptr());
if ar.currentline > 0 {
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
}
lua_pushliteral(L, " 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, 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_TNIL => {
lua_pushliteral(L, "nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
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_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);
}
}
};
} 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"));
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"));
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);
}
lua_replace(L, -2);
}
+156
View File
@@ -0,0 +1,156 @@
//! 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_Integer, 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" {
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;
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;
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_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;
// TODO: luaL_findtable
pub fn luaL_typename(L: *mut lua_State, idx: c_int) -> *const c_char;
// 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, "");
lua_getmetatable(L, -1);
lua_setreadonly(L, -1, enabled);
lua_pop(L, 2);
// set globals to readonly and activate safeenv since the env is immutable
lua_setreadonly(L, LUA_GLOBALSINDEX, enabled);
lua_setsafeenv(L, LUA_GLOBALSINDEX, enabled);
}
//
// TODO: Generic Buffer Manipulation
//
+493
View File
@@ -0,0 +1,493 @@
//! Contains definitions from `lua.h`.
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
use std::ptr;
// 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_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;
/// 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, equivalent to `i32`.
pub type lua_Integer = c_int;
/// 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" fn(L: *mut lua_State) -> c_int;
pub type lua_Continuation = unsafe extern "C" fn(L: *mut lua_State, status: c_int) -> c_int;
/// Type for userdata destructor functions.
pub type lua_Udestructor = unsafe extern "C" fn(*mut c_void);
/// 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;
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;
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) -> lua_Integer;
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_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_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);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: 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_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *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" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
//
// Memory statistics
//
extern "C" {
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" {
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);
// TODO: lua_encodepointer
pub fn lua_clock() -> c_double;
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Udestructor>);
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
}
//
// Reference system, can be used to pin objects
//
pub const LUA_NOREF: c_int = -1;
pub const LUA_REFNIL: c_int = 0;
extern "C" {
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, 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_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatatagged(L, sz, 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_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_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 str) {
use std::ffi::CString;
let c_str = CString::new(s).unwrap();
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
}
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosurek(L, f, ptr::null(), 0, None)
}
pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) {
lua_pushcclosurek(L, f, debugname, 0, None)
}
pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) {
lua_pushcclosurek(L, f, ptr::null(), nup, None)
}
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_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" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Coverage = unsafe extern "C" fn(
context: *mut c_void,
function: *const c_char,
linedefined: c_int,
depth: c_int,
hits: *const c_int,
size: usize,
);
extern "C" {
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);
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 linedefined: c_int,
pub currentline: c_int,
pub nupvals: u8,
pub nparams: u8,
pub isvararg: c_char,
pub short_src: [c_char; LUA_IDSIZE],
pub userdata: *mut c_void,
}
//
// Callbacks that can be used to reconfigure behavior of the VM dynamically.
// These are shared between all coroutines.
//
#[repr(C)]
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" 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" 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" 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" fn(s: *const c_char, l: usize) -> i16>,
/// gets called when BREAK instruction is encountered
pub debugbreak: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called after each instruction in single step mode
pub debugstep: Option<unsafe extern "C" 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" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when protected call results in an error
pub debugprotectederror: Option<unsafe extern "C" fn(L: *mut lua_State)>,
}
extern "C" {
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
}
+39
View File
@@ -0,0 +1,39 @@
//! Contains definitions from `luacode.h`.
use std::os::raw::{c_char, c_int, c_void};
use std::slice;
#[repr(C)]
pub struct lua_CompileOptions {
pub optimizationLevel: c_int,
pub debugLevel: c_int,
pub coverageLevel: c_int,
pub vectorLib: *const c_char,
pub vectorCtor: *const c_char,
pub mutableGlobals: *mut *const c_char,
}
extern "C" {
#[link_name = "luau_compile"]
pub fn luau_compile_(
source: *const c_char,
size: usize,
options: *mut lua_CompileOptions,
outsize: *mut usize,
) -> *mut c_char;
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,
);
let data = slice::from_raw_parts(data_ptr as *mut u8, outsize).to_vec();
free(data_ptr as *mut c_void);
data
}
+29
View File
@@ -0,0 +1,29 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::c_int;
use super::lua::lua_State;
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_BITLIBNAME: &str = "bit32";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
extern "C" {
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_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;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+13
View File
@@ -0,0 +1,13 @@
//! Low level bindings to Luau.
pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use luacode::*;
pub use lualib::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod luacode;
pub mod lualib;
+85 -230
View File
@@ -1,246 +1,101 @@
// The MIT License (MIT)
//
// Copyright (c) 2019 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.
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 including LuaJIT.
#![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 lua54::*;
#[cfg(feature = "lua53")]
pub use self::lua::{lua_KContext, lua_KFunction};
pub use lua53::*;
#[cfg(feature = "lua52")]
pub use lua52::*;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::lua_setfenv;
pub use lua51::*;
// 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 = "luau")]
pub use luau::*;
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
#[cfg(feature = "lua53")]
pub use self::lua::{lua_isyieldable, lua_version};
// 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 = "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 = "lua53", feature = "lua52"))]
pub use self::lualib::{luaopen_bit32, luaopen_coroutine};
#[cfg(feature = "lua53")]
pub use self::lualib::luaopen_utf8;
// 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_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE, LUA_TNUMBER, LUA_TSTRING,
LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
};
#[cfg(any(feature = "lua53", feature = "lua52"))]
pub use self::lua::{LUA_ERRGCMM, LUA_GCISRUNNING, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD};
#[cfg(feature = "lua53")]
pub use self::lua::{
LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPIDIV, LUA_OPSHL, LUA_OPSHR,
};
#[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 = "lua53", feature = "lua52"))]
pub use self::lualib::LUA_BITLIBNAME;
#[cfg(feature = "lua53")]
pub use self::lualib::LUA_UTF8LIBNAME;
// Not actually defined in lua.h / luaconf.h
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub const LUA_MAX_UPVALUES: c_int = 255;
#[allow(unused_imports, dead_code, non_camel_case_types)]
mod glue {
include!(concat!(env!("OUT_DIR"), "/glue.rs"));
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "vendored"))))]
pub const LUA_MAX_UPVALUES: c_int = 60;
#[cfg(all(feature = "luajit", feature = "vendored"))]
pub const LUA_MAX_UPVALUES: c_int = 120;
#[cfg(feature = "luau")]
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`
pub const LUA_TRACEBACK_STACK: c_int = 11;
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
// 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",
all(target_arch = "riscv32", not(target_os = "espidf")),
all(target_arch = "xtensa", not(target_os = "espidf")),
)))]
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",
target_arch = "wasm64",
)))]
pub const SYS_MIN_ALIGN: usize = 16;
// The allocator on the esp-idf platform guarentees 4 byte alignment.
#[cfg(all(any(
all(target_arch = "riscv32", target_os = "espidf"),
all(target_arch = "xtensa", target_os = "espidf"),
)))]
pub const SYS_MIN_ALIGN: usize = 4;
// Hack to avoid stripping a few unused Lua symbols that could be imported
// by C modules in unsafe mode
#[cfg(not(feature = "luau"))]
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(feature = "lua54", feature = "lua53", feature = "lua52")) {
symbols.push(lua_getglobal as _);
symbols.push(lua_setglobal as _);
}
}
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
mod compat53;
#[cfg(feature = "lua54")]
pub mod lua54;
mod lauxlib;
mod lua;
mod luaconf;
mod lualib;
#[cfg(feature = "lua53")]
pub mod lua53;
#[cfg(feature = "lua52")]
pub mod lua52;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub mod lua51;
#[cfg(feature = "luau")]
pub mod luau;
+155 -8
View File
@@ -1,3 +1,4 @@
use std::mem;
use std::os::raw::c_int;
use std::ptr;
@@ -5,14 +6,29 @@ 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_closure, StackGuard,
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
#[derive(Clone, Debug)]
pub struct FunctionInfo {
pub name: Option<Vec<u8>>,
pub name_what: Option<Vec<u8>>,
pub what: Option<Vec<u8>>,
pub source: Option<Vec<u8>>,
pub short_src: Option<Vec<u8>>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
}
impl<'lua> Function<'lua> {
/// Calls the function, passing `args` as function arguments.
///
@@ -57,7 +73,7 @@ impl<'lua> Function<'lua> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua;
let args = args.to_lua_multi(lua)?;
let mut args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
@@ -67,7 +83,7 @@ impl<'lua> Function<'lua> {
ffi::lua_pushcfunction(lua.state, error_traceback);
let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(&self.0);
for arg in args {
for arg in args.drain_all() {
lua.push_value(arg)?;
}
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
@@ -75,7 +91,7 @@ impl<'lua> Function<'lua> {
return Err(pop_error(lua.state, ret));
}
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = MultiValue::new();
let mut results = args; // Reuse MultiValue container
assert_stack(lua.state, 2);
for _ in 0..nresults {
results.push_front(lua.pop_value());
@@ -86,6 +102,54 @@ impl<'lua> Function<'lua> {
R::from_lua_multi(results, lua)
}
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
/// and drives the execution.
///
/// Internally it wraps the function to an [`AsyncThread`].
///
/// Requires `feature = "async"`
///
/// # Examples
///
/// ```
/// 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;
/// Ok(())
/// })?;
///
/// sleep.call_async(10).await?;
///
/// # Ok(())
/// # }
/// ```
///
/// [`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>>
where
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
match lua.create_recycled_thread(self.clone()) {
Ok(t) => {
let mut t = t.into_async(args);
t.set_recyclable(true);
Box::pin(t)
}
Err(e) => Box::pin(future::err(e)),
}
}
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
/// arguments.
///
@@ -146,17 +210,100 @@ impl<'lua> Function<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 5)?;
lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(arg)?;
}
protect_lua_closure(lua.state, nargs + 2, 1, |state| {
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
})?;
Ok(Function(lua.pop_ref()))
}
}
/// 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;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
let mut ar: ffi::lua_Debug = mem::zeroed();
lua.push_ref(&self.0);
#[cfg(not(feature = "luau"))]
let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar);
#[cfg(feature = "luau")]
let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar);
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()),
#[cfg(feature = "luau")]
name_what: None,
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
short_src: ptr_to_cstr_bytes(&ar.short_src as *const _).map(|s| s.to_vec()),
line_defined: ar.linedefined as i32,
#[cfg(not(feature = "luau"))]
last_line_defined: ar.lastlinedefined as i32,
}
}
}
/// Dumps the function as a binary chunk.
///
/// 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> {
use std::os::raw::c_void;
use std::slice;
unsafe extern "C" fn writer(
_state: *mut ffi::lua_State,
buf: *const c_void,
buf_len: usize,
data: *mut c_void,
) -> c_int {
let data = &mut *(data as *mut Vec<u8>);
let buf = slice::from_raw_parts(buf as *const u8, buf_len);
data.extend_from_slice(buf);
0
}
let lua = self.0.lua;
let mut data: Vec<u8> = Vec::new();
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.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);
}
data
}
}
impl<'lua> PartialEq for Function<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
+351
View File
@@ -0,0 +1,351 @@
use std::cell::UnsafeCell;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
use std::os::raw::c_int;
use crate::ffi::{self, lua_Debug};
use crate::lua::Lua;
use crate::util::ptr_to_cstr_bytes;
/// 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 [documentation][lua_doc].
///
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
/// [`Lua::set_hook`]: crate::Lua::set_hook
pub struct Debug<'lua> {
lua: &'lua Lua,
ar: ActivationRecord,
#[cfg(feature = "luau")]
level: c_int,
}
impl<'lua> Debug<'lua> {
#[cfg(not(feature = "luau"))]
pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
Debug {
lua,
ar: ActivationRecord::Borrowed(ar),
}
}
pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
Debug {
lua,
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<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
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_cstr_bytes((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
#[cfg(feature = "luau")]
name_what: None,
}
}
}
/// Corresponds to the `S` what mask.
pub fn source(&self) -> DebugSource<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
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_cstr_bytes((*self.ar.get()).source),
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
line_defined: (*self.ar.get()).linedefined as i32,
#[cfg(not(feature = "luau"))]
last_line_defined: (*self.ar.get()).lastlinedefined as i32,
what: ptr_to_cstr_bytes((*self.ar.get()).what),
}
}
}
/// 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.lua.state, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
#[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 as i32
}
}
/// 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.lua.state, cstr!("t"), self.ar.get()) != 0,
"lua_getinfo failed with `t`"
);
(*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.lua.state, cstr!("u"), self.ar.get()) != 0,
"lua_getinfo failed with `u`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
"lua_getinfo failed with `a`"
);
#[cfg(not(feature = "luau"))]
let stack = DebugStack {
num_ups: (*self.ar.get()).nups as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
num_params: (*self.ar.get()).nparams as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
is_vararg: (*self.ar.get()).isvararg != 0,
};
#[cfg(feature = "luau")]
let stack = DebugStack {
num_ups: (*self.ar.get()).nupvals as i32,
num_params: (*self.ar.get()).nparams as i32,
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]>,
}
#[derive(Clone, Debug)]
pub struct DebugSource<'a> {
pub source: Option<&'a [u8]>,
pub short_src: Option<&'a [u8]>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
pub what: Option<&'a [u8]>,
}
#[derive(Copy, Clone, Debug)]
pub struct DebugStack {
pub num_ups: i32,
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
pub num_params: i32,
/// 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.
pub on_calls: bool,
/// When Lua returns from a function.
pub on_returns: bool,
/// Before executing a new line, or returning from a function call.
pub every_line: bool,
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
/// `count` is the number of VM instructions to execute before calling the hook.
///
/// # Performance
///
/// Setting this option to a low value can incur a very high overhead.
pub every_nth_instruction: Option<u32>,
}
#[cfg(not(feature = "luau"))]
impl HookTriggers {
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
///
/// [`on_calls`]: #structfield.on_calls
pub fn on_calls() -> Self {
HookTriggers {
on_calls: true,
..Default::default()
}
}
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
///
/// [`on_returns`]: #structfield.on_returns
pub fn on_returns() -> Self {
HookTriggers {
on_returns: true,
..Default::default()
}
}
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
///
/// [`every_line`]: #structfield.every_line
pub fn every_line() -> Self {
HookTriggers {
every_line: true,
..Default::default()
}
}
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
///
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
pub fn every_nth_instruction(n: u32) -> Self {
HookTriggers {
every_nth_instruction: Some(n),
..Default::default()
}
}
// Compute the mask to pass to `lua_sethook`.
pub(crate) fn mask(&self) -> c_int {
let mut mask: c_int = 0;
if self.on_calls {
mask |= ffi::LUA_MASKCALL
}
if self.on_returns {
mask |= ffi::LUA_MASKRET
}
if self.every_line {
mask |= ffi::LUA_MASKLINE
}
if self.every_nth_instruction.is_some() {
mask |= ffi::LUA_MASKCOUNT
}
mask
}
// 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
}
}
#[cfg(not(feature = "luau"))]
impl BitOr for HookTriggers {
type Output = Self;
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;
}
}
+167 -16
View File
@@ -23,53 +23,204 @@
//!
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
//! Fields are supported using the [`UserDataFields`] API.
//!
//! # 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.
//! 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.
//!
//! 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).
//!
//! 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`].
//!
//! [Lua programming language]: https://www.lua.org/
//! [`Lua`]: struct.Lua.html
//! [executing]: struct.Lua.html#method.exec
//! [evaluating]: struct.Lua.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
//! [`UserData`]: trait.UserData.html
//! [`UserDataMethods`]: trait.UserDataMethods.html
//! [`Lua`]: crate::Lua
//! [executing]: crate::Chunk::exec
//! [evaluating]: crate::Chunk::eval
//! [globals]: crate::Lua::globals
//! [`ToLua`]: crate::ToLua
//! [`FromLua`]: crate::FromLua
//! [`ToLuaMulti`]: crate::ToLuaMulti
//! [`FromLuaMulti`]: crate::FromLuaMulti
//! [`Function`]: crate::Function
//! [`UserData`]: crate::UserData
//! [`UserDataFields`]: crate::UserDataFields
//! [`UserDataMethods`]: crate::UserDataMethods
//! [`LuaSerdeExt`]: crate::LuaSerdeExt
//! [`Value`]: crate::Value
//! [`create_async_function`]: crate::Lua::create_async_function
//! [`call_async`]: crate::Function::call_async
//! [`AsyncThread`]: crate::AsyncThread
//! [`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.8.0-beta.4")]
// 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))]
#[macro_use]
mod macros;
mod chunk;
mod conversion;
mod error;
mod ffi;
mod function;
mod hook;
mod lua;
#[cfg(feature = "luau")]
mod luau;
mod multi;
mod scope;
mod stdlib;
mod string;
mod table;
mod thread;
mod types;
mod userdata;
mod userdata_impl;
mod util;
mod value;
pub use crate::ffi::lua_State;
pub mod prelude;
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::Function;
pub use crate::lua::{Chunk, Lua};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::lua::{GCMode, Lua, LuaOptions};
pub use crate::multi::Variadic;
pub use crate::scope::Scope;
pub use crate::stdlib::StdLib;
pub use crate::string::String;
pub use crate::table::{Table, TablePairs, TableSequence};
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
pub mod prelude;
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{chunk::Compiler, types::VmState};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
#[cfg(feature = "serialize")]
#[doc(inline)]
pub use crate::serde::{
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
};
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub mod serde;
#[cfg(any(feature = "mlua_derive"))]
#[allow(unused_imports)]
#[macro_use]
extern crate mlua_derive;
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
///
/// 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's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
///
/// Captured variables are **moved** into the chunk.
///
/// ```
/// use mlua::{Lua, Result, chunk};
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// let name = "Rustacean";
/// lua.load(chunk! {
/// print("hello, " .. $name)
/// }).exec()
/// }
/// ```
///
/// ## Syntax issues
///
/// Since the Rust tokenizer will tokenize Lua code, this imposes some restrictions.
/// The main thing to remember is:
///
/// - Use double quoted strings (`""`) instead of single quoted strings (`''`).
///
/// (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 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`.
///
/// - The `//` (floor division) operator is unusable, as its start a comment.
///
/// Everything else should work.
///
/// [`AsChunk`]: crate::AsChunk
/// [`UserData`]: crate::UserData
/// [`ToLua`]: crate::ToLua
#[cfg(any(feature = "macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::chunk;
/// 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`.
///
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
+2420 -661
View File
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
use std::ffi::CStr;
use std::os::raw::{c_float, c_int};
use crate::chunk::ChunkMode;
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::util::{check_stack, StackGuard};
use crate::value::Value;
// Since Luau has some missing standard function, we re-implement them here
impl Lua {
pub(crate) unsafe fn prepare_luau_state(&self) -> Result<()> {
let globals = self.globals();
globals.raw_set(
"collectgarbage",
self.create_c_function(lua_collectgarbage)?,
)?;
globals.raw_set("require", self.create_function(lua_require)?)?;
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
Ok(())
}
}
unsafe extern "C" 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);
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")),
}
}
fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
// Find module in the cache
let loaded = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
protect_lua!(lua.state, 0, 1, fn(state) {
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
})?;
Table(lua.pop_ref())
};
if let Some(v) = loaded.raw_get(name.clone())? {
return Ok(v);
}
// Load file from filesystem
let mut search_path = std::env::var("LUAU_PATH").unwrap_or_default();
if search_path.is_empty() {
search_path = "?.luau;?.lua".into();
}
let mut source = None;
for path in search_path.split(';') {
if let Ok(buf) = std::fs::read(path.replacen('?', &name, 1)) {
source = Some(buf);
break;
}
}
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{}'", name)))?;
let value = lua
.load(&source)
.set_name(&format!("={}", name))?
.set_mode(ChunkMode::Text)
.call::<_, Value>(())?;
// Save in the cache
loaded.raw_set(
name,
match value.clone() {
Value::Nil => Value::Boolean(true),
v => v,
},
)?;
Ok(value)
}
// Luau vector datatype constructor
unsafe extern "C" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
let x = ffi::luaL_checknumber(state, 1) as c_float;
let y = ffi::luaL_checknumber(state, 2) as c_float;
let z = ffi::luaL_checknumber(state, 3) as c_float;
ffi::lua_pushvector(state, x, y, z);
1
}
+35 -4
View File
@@ -17,19 +17,19 @@ macro_rules! cstr {
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),+)
};
}
@@ -78,3 +78,34 @@ macro_rules! mlua_expect {
mlua_expect!($res, $msg)
};
}
#[cfg(feature = "module")]
#[doc(hidden)]
#[macro_export]
macro_rules! require_module_feature {
() => {};
}
#[cfg(not(feature = "module"))]
#[doc(hidden)]
#[macro_export]
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) => {{
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
$code;
$nresults
}
crate::util::protect_lua_call($state, $nargs, do_call)
}};
}
+22 -15
View File
@@ -1,3 +1,5 @@
#![allow(clippy::wrong_self_convention)]
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult;
@@ -10,8 +12,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// 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();
let mut result = MultiValue::new_or_cached(lua);
match self {
Ok(v) => result.push_front(v.to_lua(lua)?),
Err(e) => {
@@ -19,14 +20,13 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
result.push_front(Nil);
}
}
Ok(result)
}
}
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();
let mut v = MultiValue::new_or_cached(lua);
v.push_front(self.to_lua(lua)?);
Ok(v)
}
@@ -34,7 +34,9 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
Ok(T::from_lua(values.pop_front().unwrap_or(Nil), lua)?)
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
lua.cache_multivalue(values);
res
}
}
@@ -74,8 +76,8 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
/// # }
/// ```
///
/// [`FromLua`]: trait.FromLua.html
/// [`MultiValue`]: struct.MultiValue.html
/// [`FromLua`]: crate::FromLua
/// [`MultiValue`]: crate::MultiValue
#[derive(Debug, Clone)]
pub struct Variadic<T>(Vec<T>);
@@ -123,30 +125,35 @@ impl<T> DerefMut for Variadic<T> {
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()
let mut values = MultiValue::new_or_cached(lua);
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
Ok(values)
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
values
.into_iter()
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let res = values
.drain_all()
.map(|e| T::from_lua(e, lua))
.collect::<Result<Vec<T>>>()
.map(Variadic)
.map(Variadic);
lua.cache_multivalue(values);
res
}
}
macro_rules! impl_tuple {
() => (
impl<'lua> ToLuaMulti<'lua> for () {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new())
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_cached(lua))
}
}
impl<'lua> FromLuaMulti<'lua> for () {
fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result<Self> {
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
lua.cache_multivalue(values);
Ok(())
}
}
+28 -5
View File
@@ -1,12 +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, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, 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, Scope as LuaScope, String as LuaString,
Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
UserData as LuaUserData, UserDataMethods as LuaUserDataMethods, Value as LuaValue,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, 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,
};
#[cfg(not(feature = "luau"))]
#[doc(no_inline)]
pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::VmState as LuaVmState;
#[cfg(feature = "async")]
#[doc(no_inline)]
pub use crate::AsyncThread as LuaAsyncThread;
#[cfg(feature = "serialize")]
#[doc(no_inline)]
pub use crate::{
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
SerializeOptions as LuaSerializeOptions,
};
+562 -144
View File
@@ -1,35 +1,51 @@
use std::any::Any;
use std::cell::Cell;
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::os::raw::{c_int, c_void};
use std::rc::Rc;
#[cfg(feature = "serialize")]
use serde::Serialize;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::types::{Callback, LuaRef};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
take_userdata, StackGuard,
};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
#[cfg(feature = "lua54")]
use crate::userdata::USER_VALUE_MAXSLOT;
#[cfg(feature = "async")]
use {
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
futures_core::future::Future,
futures_util::future::{self, TryFutureExt},
};
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
/// callbacks that are not required to be Send or 'static.
///
/// See [`Lua::scope`] for more details.
///
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`Lua::scope`]: crate::Lua.html::scope
pub struct Scope<'lua, 'scope> {
lua: &'lua Lua,
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Box<dyn Any>)>>,
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
_scope_invariant: PhantomData<Cell<&'scope ()>>,
}
type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
impl<'lua, 'scope> Scope<'lua, 'scope> {
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
Scope {
@@ -42,10 +58,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] for more details.
/// scope drop. See [`Lua::scope`] for more details.
///
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`Lua::create_function`]: crate::Lua::create_function
/// [`Lua::scope`]: crate::Lua::scope
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
@@ -54,7 +70,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
{
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
// callback itself must be 'scope lifetime, so the function should not be able to capture
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
// the 'callback lifetime here can't be enlarged due to coming from a universal
// quantification in Lua::scope.
//
@@ -71,10 +87,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
///
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`Lua::create_function_mut`]: crate::Lua::create_function_mut
/// [`Lua::scope`]: crate::Lua::scope
/// [`Scope::create_function`]: #method.create_function
pub fn create_function_mut<'callback, A, R, F>(
&'callback self,
@@ -87,37 +103,126 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
(&mut *func
(*func
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
})
}
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
///
/// Requires `feature = "async"`
///
/// [`Lua::create_async_function`]: crate::Lua::create_async_function
/// [`Lua::scope`]: crate::Lua::scope
/// [`Lua::async_scope`]: crate::Lua::async_scope
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn create_async_function<'callback, A, R, F, FR>(
&'callback self,
func: F,
) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'callback Lua, A) -> FR,
FR: 'callback + Future<Output = Result<R>>,
{
unsafe {
self.create_async_callback(Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
}))
}
}
/// Create a Lua userdata object from a custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the
/// UserData be 'static). See [`Lua::scope`] for more details.
/// UserData be 'static).
/// See [`Lua::scope`] for more details.
///
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_static_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
/// [`Lua::create_userdata`]: crate::Lua::create_userdata
/// [`Lua::scope`]: crate::Lua::scope
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: 'static + UserData,
{
self.create_userdata_inner(UserDataCell::new(data))
}
/// Create a Lua userdata object from a custom serializable userdata type.
///
/// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the
/// UserData be 'static).
/// See [`Lua::scope`] for more details.
///
/// Requires `feature = "serialize"`
///
/// [`Lua::create_ser_userdata`]: crate::Lua::create_ser_userdata
/// [`Lua::scope`]: crate::Lua::scope
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: 'static + UserData + Serialize,
{
self.create_userdata_inner(UserDataCell::new_ser(data))
}
fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
where
T: 'static + UserData,
{
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
// thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe {
let u = self.lua.make_userdata(data)?;
self.destructors.borrow_mut().push((u.0.clone(), |u| {
let state = u.lua.state;
let ud = self.lua.make_userdata(data)?;
#[cfg(any(feature = "lua51", feature = "luajit"))]
let newtable = self.lua.create_table()?;
let destructor: DestructorCallback = Box::new(move |ud| {
let state = ud.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
u.lua.push_ref(&u);
// We know the destructor has not run yet because we hold a reference to the
// userdata.
Box::new(take_userdata::<RefCell<T>>(state))
}));
Ok(u)
// Check that userdata is not destructed (via `take()` call)
if ud.lua.push_userdata_ref(&ud).is_err() {
return vec![];
}
// Clear associated user values
#[cfg(feature = "lua54")]
for i in 1..=USER_VALUE_MAXSLOT {
ffi::lua_pushnil(state);
ffi::lua_setiuservalue(state, -2, i as c_int);
}
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
{
ffi::lua_pushnil(state);
ffi::lua_setuservalue(state, -2);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
{
ud.lua.push_ref(&newtable.0);
ffi::lua_setuservalue(state, -2);
}
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
});
self.destructors
.borrow_mut()
.push((ud.0.clone(), destructor));
Ok(ud)
}
}
@@ -129,21 +234,21 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
///
/// Lifting the requirement that the UserData type be 'static comes with some important
/// limitations, so if you only need to eliminate the Send requirement, it is probably better to
/// use [`Scope::create_static_userdata`] instead.
/// use [`Scope::create_userdata`] instead.
///
/// The main limitation that comes from using non-'static userdata is that the produced userdata
/// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for
/// 'static types. This means that it is impossible, once the userdata is created, to get a
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
/// will no longer have a `TypeId` associated with it, because `TypeId` can only work for
/// 'static types. This means that it is impossible, once the userdata is created, to get a
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
/// a single metatable for multiple non-'static types, so there is a higher cost associated with
/// creating the userdata metatable each time a new userdata is created.
///
/// [`create_static_userdata`]: #method.create_static_userdata
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`UserDataMethods`]: trait.UserDataMethods.html
/// [`Scope::create_userdata`]: #method.create_userdata
/// [`Lua::create_userdata`]: crate::Lua::create_userdata
/// [`Lua::scope`]:crate::Lua::scope
/// [`UserDataMethods`]: crate::UserDataMethods
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: 'scope + UserData,
@@ -152,72 +257,56 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// 'callback outliving 'scope is a lie to make the types work out, required due to the
// inability to work with the more correct callback type that is universally quantified over
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
// lifetime, so none of the static methods UserData types can add can possibly capture
// parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &Scope<'lua, 'scope>,
data: Rc<RefCell<T>>,
ud_ptr: *const c_void,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in
// userdata, where we pretend there is a unique type per call to
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
// it on a mismatched userdata type, which when using normal 'static userdata will fail
// with a type mismatch, but here without this check would proceed as though you had
// called the method on the original value (since we otherwise completely ignore the
// first argument).
let check_data = data.clone();
let check_ud_type = move |lua: &'callback Lua, value| {
if let Some(value) = value {
if let Value::UserData(u) = value {
unsafe {
assert_stack(lua.state, 1);
lua.push_ref(&u.0);
ffi::lua_getuservalue(lua.state, -1);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
{
ffi::lua_pushinteger(lua.state, 1);
ffi::lua_gettable(lua.state, -2);
ffi::lua_remove(lua.state, -2);
}
return ffi::lua_touserdata(lua.state, -1)
== check_data.as_ptr() as *mut c_void;
if let Some(Value::UserData(ud)) = value {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
lua.push_userdata_ref(&ud.0)?;
if get_userdata(lua.state, -1) as *const _ == ud_ptr {
return Ok(());
}
}
}
false
};
Err(Error::UserDataTypeMismatch)
};
match method {
NonStaticMethod::Method(method) => {
let method_data = data.clone();
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
if !check_ud_type(lua, args.pop_front()) {
return Err(Error::UserDataTypeMismatch);
}
let data = method_data
.try_borrow()
.map_err(|_| Error::UserDataBorrowError)?;
check_ud_type(lua, args.pop_front())?;
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &*data, args)
});
unsafe { scope.create_callback(f) }
}
NonStaticMethod::MethodMut(method) => {
let method = RefCell::new(method);
let method_data = data.clone();
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
if !check_ud_type(lua, args.pop_front()) {
return Err(Error::UserDataTypeMismatch);
}
check_ud_type(lua, args.pop_front())?;
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let mut data = method_data
let mut data = data
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
(&mut *method)(lua, &mut *data, args)
(*method)(lua, &mut *data, args)
});
unsafe { scope.create_callback(f) }
}
@@ -225,7 +314,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
NonStaticMethod::FunctionMut(function) => {
let function = RefCell::new(function);
let f = Box::new(move |lua, args| {
(&mut *function
(*function
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?)(
lua, args
@@ -236,68 +325,169 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
}
}
let mut ud_fields = NonStaticUserDataFields::default();
let mut ud_methods = NonStaticUserDataMethods::default();
T::add_fields(&mut ud_fields);
T::add_methods(&mut ud_methods);
unsafe {
let lua = self.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
check_stack(lua.state, 13)?;
push_userdata(lua.state, ())?;
#[cfg(feature = "lua53")]
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
protect_lua_closure(lua.state, 0, 1, |state| {
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
ffi::lua_createtable(state, 1, 0);
ffi::lua_pushinteger(state, 1);
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
ffi::lua_settable(state, -3);
})?;
ffi::lua_setuservalue(lua.state, -2);
#[cfg(not(feature = "luau"))]
#[allow(clippy::let_and_return)]
let ud_ptr = protect_lua!(lua.state, 0, 1, |state| {
let ud =
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>());
protect_lua_closure(lua.state, 0, 1, move |state| {
ffi::lua_newtable(state);
})?;
for (k, m) in ud_methods.meta_methods {
push_string(lua.state, k.name())?;
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
protect_lua_closure(lua.state, 3, 1, |state| {
ffi::lua_rawset(state, -3);
})?;
}
if ud_methods.methods.is_empty() {
init_userdata_metatable::<()>(lua.state, -1, None)?;
} else {
protect_lua_closure(lua.state, 0, 1, |state| {
// Set empty environment for Lua 5.1
#[cfg(any(feature = "lua51", feature = "luajit"))]
{
ffi::lua_newtable(state);
})?;
for (k, m) in ud_methods.methods {
push_string(lua.state, &k)?;
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
protect_lua_closure(lua.state, 3, 1, |state| {
ffi::lua_rawset(state, -3);
})?;
ffi::lua_setuservalue(state, -2);
}
init_userdata_metatable::<()>(lua.state, -2, Some(-1))?;
ffi::lua_pop(lua.state, 1);
ud
})?;
#[cfg(feature = "luau")]
let ud_ptr = {
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
UserDataCell::new(data.clone()),
)?;
ffi::lua_touserdata(lua.state, -1)
};
// Prepare metatable, add meta methods first and then meta fields
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
push_table(lua.state, 0, meta_methods_nrec as c_int)?;
for (k, m) in ud_methods.meta_methods {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, k.validate()?.name())?;
}
for (k, f) in ud_fields.meta_fields {
lua.push_value(f(mem::transmute(lua))?)?;
rawset_field(lua.state, -2, k.validate()?.name())?;
}
let metatable_index = ffi::lua_absindex(lua.state, -1);
let mut field_getters_index = None;
let field_getters_nrec = ud_fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(lua.state, 0, field_getters_nrec as c_int)?;
for (k, m) in ud_fields.field_getters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, &k)?;
}
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
}
ffi::lua_setmetatable(lua.state, -2);
let mut field_setters_index = None;
let field_setters_nrec = ud_fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(lua.state, 0, field_setters_nrec as c_int)?;
for (k, m) in ud_fields.field_setters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, &k)?;
}
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
}
Ok(AnyUserData(lua.pop_ref()))
let mut methods_index = None;
let methods_nrec = ud_methods.methods.len();
if methods_nrec > 0 {
// Create table used for methods lookup
push_table(lua.state, 0, methods_nrec as c_int)?;
for (k, m) in ud_methods.methods {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, &k)?;
}
methods_index = Some(ffi::lua_absindex(lua.state, -1));
}
init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
metatable_index,
field_getters_index,
field_setters_index,
methods_index,
)?;
let count = field_getters_index.map(|_| 1).unwrap_or(0)
+ field_setters_index.map(|_| 1).unwrap_or(0)
+ methods_index.map(|_| 1).unwrap_or(0);
ffi::lua_pop(lua.state, count);
let mt_ptr = ffi::lua_topointer(lua.state, -1);
// Write userdata just before attaching metatable with `__gc` metamethod
#[cfg(not(feature = "luau"))]
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
ffi::lua_setmetatable(lua.state, -2);
let ud = AnyUserData(lua.pop_ref());
lua.register_userdata_metatable(mt_ptr, None);
#[cfg(any(feature = "lua51", feature = "luajit"))]
let newtable = lua.create_table()?;
let destructor: DestructorCallback = Box::new(move |ud| {
let state = ud.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
// Check that userdata is valid (very likely)
if ud.lua.push_userdata_ref(&ud).is_err() {
return vec![];
}
// Deregister metatable
ffi::lua_getmetatable(state, -1);
let mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ud.lua.deregister_userdata_metatable(mt_ptr);
// Clear associated user values
#[cfg(feature = "lua54")]
for i in 1..=USER_VALUE_MAXSLOT {
ffi::lua_pushnil(state);
ffi::lua_setiuservalue(state, -2, i as c_int);
}
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
{
ffi::lua_pushnil(state);
ffi::lua_setuservalue(state, -2);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
{
ud.lua.push_ref(&newtable.0);
ffi::lua_setuservalue(state, -2);
}
// A hack to drop non-static `T`
unsafe fn seal<T>(t: T) -> Box<dyn FnOnce() + 'static> {
let f: Box<dyn FnOnce()> = Box::new(move || drop(t));
mem::transmute(f)
}
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
vec![ud]
});
self.destructors
.borrow_mut()
.push((ud.0.clone(), destructor));
Ok(ud)
}
}
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
// must NOT capture any parameters.
unsafe fn create_callback<'callback>(
&self,
@@ -306,22 +496,84 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
let f = self.lua.create_callback(f)?;
let mut destructors = self.destructors.borrow_mut();
destructors.push((f.0.clone(), |f| {
let destructor: DestructorCallback = Box::new(|f| {
let state = f.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 3);
f.lua.push_ref(&f);
ffi::lua_getupvalue(state, -1, 1);
// We know the destructor has not run yet because we hold a reference to the callback.
let ud = take_userdata::<Callback>(state);
ffi::lua_getupvalue(state, -1, 1);
let ud = take_userdata::<CallbackUpvalue>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1);
vec![Box::new(ud)]
});
self.destructors
.borrow_mut()
.push((f.0.clone(), destructor));
Ok(f)
}
#[cfg(feature = "async")]
unsafe fn create_async_callback<'callback>(
&self,
f: AsyncCallback<'callback, 'scope>,
) -> Result<Function<'lua>> {
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
let f = self.lua.create_async_callback(f)?;
// We need to pre-allocate strings to avoid failures in destructor.
let get_poll_str = self.lua.create_string("get_poll")?;
let poll_str = self.lua.create_string("poll")?;
let destructor: DestructorCallback = Box::new(move |f| {
let state = f.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 5);
f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback.
// First, get the environment table
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_getupvalue(state, -1, 1);
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
ffi::lua_getfenv(state, -1);
// Second, get the `get_poll()` closure using the corresponding key
f.lua.push_ref(&get_poll_str.0);
ffi::lua_rawget(state, -2);
// Destroy all upvalues
ffi::lua_getupvalue(state, -1, 1);
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1);
ffi::lua_pop(state, 1);
Box::new(ud)
}));
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
// Finally, get polled future and destroy it
f.lua.push_ref(&poll_str.0);
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
ffi::lua_getupvalue(state, -1, 1);
let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1);
data.push(Box::new(upvalue2));
}
data
});
self.destructors
.borrow_mut()
.push((f.0.clone(), destructor));
Ok(f)
}
}
@@ -329,7 +581,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
fn drop(&mut self) {
// We separate the action of invalidating the userdata in Lua and actually dropping the
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
// can be sure that all of the userdata in Lua is actually invalidated.
// All destructors are non-panicking, so this is fine
@@ -337,7 +589,7 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
.destructors
.get_mut()
.drain(..)
.map(|(r, dest)| dest(r))
.flat_map(|(r, dest)| dest(r))
.collect::<Vec<_>>();
drop(to_drop);
@@ -368,10 +620,10 @@ impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
@@ -383,10 +635,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
@@ -396,12 +648,27 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
));
}
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
#[cfg(feature = "async")]
fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
where
S: ?Sized + AsRef<[u8]>,
T: Clone,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
mlua_panic!("asynchronous methods are not supported for non-static userdata")
}
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
@@ -413,10 +680,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.methods.push((
name.as_ref().to_vec(),
@@ -426,59 +693,210 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
));
}
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
#[cfg(feature = "async")]
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
mlua_panic!("asynchronous functions are not supported for non-static userdata")
}
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.meta_methods.push((
meta,
meta.into(),
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, mut method: M)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.meta_methods.push((
meta,
meta.into(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<S, A, R, M, MR>(&mut self, _meta: S, _method: M)
where
T: Clone,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
mlua_panic!("asynchronous meta methods are not supported for non-static userdata")
}
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.meta_methods.push((
meta,
meta.into(),
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, mut function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.meta_methods.push((
meta,
meta.into(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_function<S, A, R, F, FR>(&mut self, _meta: S, _function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
mlua_panic!("asynchronous meta functions are not supported for non-static userdata")
}
}
struct NonStaticUserDataFields<'lua, T: UserData> {
field_getters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
field_setters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
#[allow(clippy::type_complexity)]
meta_fields: Vec<(MetaMethod, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
}
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
fn default() -> NonStaticUserDataFields<'lua, T> {
NonStaticUserDataFields {
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
}
}
}
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
{
self.field_getters.push((
name.as_ref().to_vec(),
NonStaticMethod::Method(Box::new(move |lua, ud, _| {
method(lua, ud)?.to_lua_multi(lua)
})),
));
}
fn add_field_method_set<S, A, M>(&mut self, name: &S, mut method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
{
self.field_setters.push((
name.as_ref().to_vec(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
{
self.field_getters.push((
name.as_ref().to_vec(),
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
{
self.field_setters.push((
name.as_ref().to_vec(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
let (ud, val) = <_>::from_lua_multi(args, lua)?;
function(lua, ud, val)?.to_lua_multi(lua)
})),
));
}
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
where
S: Into<MetaMethod>,
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
R: ToLua<'lua>,
{
let meta = meta.into();
self.meta_fields.push((
meta.clone(),
Box::new(move |lua| {
let value = f(lua)?.to_lua(lua)?;
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
match value {
Value::Nil | Value::Table(_) | Value::Function(_) => {}
_ => {
return Err(Error::MetaMethodTypeError {
method: meta.to_string(),
type_name: value.type_name(),
message: Some("expected nil, table or function".to_string()),
})
}
}
}
Ok(value)
}),
));
}
}
+603
View File
@@ -0,0 +1,603 @@
use std::cell::RefCell;
use std::convert::TryInto;
use std::os::raw::c_void;
use std::rc::Rc;
use std::string::String as StdString;
use rustc_hash::FxHashSet;
use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result};
use crate::ffi;
use crate::table::{Table, TablePairs, TableSequence};
use crate::value::Value;
/// A struct for deserializing Lua values into Rust values.
#[derive(Debug)]
pub struct Deserializer<'lua> {
value: Value<'lua>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
/// 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 [`Thread`], [`UserData`], [`LightUserData`]
/// and [`Error`] will cause an error.
/// Otherwise these types skipped when iterating or serialized as unit type.
///
/// Default: **true**
///
/// [`Thread`]: crate::Thread
/// [`UserData`]: crate::UserData
/// [`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,
}
impl Default for Options {
fn default() -> Self {
Self::new()
}
}
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,
}
}
/// 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 fn deny_recursive_tables(mut self, enabled: bool) -> Self {
self.deny_recursive_tables = enabled;
self
}
}
impl<'lua> Deserializer<'lua> {
/// Creates a new Lua Deserializer for the `Value`.
pub fn new(value: Value<'lua>) -> 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<'lua>, options: Options) -> Self {
Deserializer {
value,
options,
visited: Rc::new(RefCell::new(FxHashSet::default())),
}
}
fn from_parts(
value: Value<'lua>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
) -> Self {
Deserializer {
value,
options,
visited,
}
}
}
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
type Error = Error;
#[inline]
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
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.try_into().expect("cannot convert lua_Integer to i64"))
}
#[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()),
},
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
Value::Table(_) => self.deserialize_map(visitor),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
if self.options.deny_unsupported_types {
Err(de::Error::custom(format!(
"unsupported value type `{}`",
self.value.type_name()
)))
} else {
visitor.visit_unit()
}
}
}
}
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::Nil => visitor.visit_none(),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
_ => visitor.visit_some(self),
}
}
#[inline]
fn deserialize_enum<V>(
self,
_name: &str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
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 => {
return Err(de::Error::invalid_value(
de::Unexpected::Map,
&"map with a single key",
))
}
};
if iter.next().is_some() {
return Err(de::Error::invalid_value(
de::Unexpected::Map,
&"map with a single key",
));
}
if check_value_if_skip(&value, self.options, &self.visited)? {
return Err(de::Error::custom("bad enum value"));
}
(variant, Some(value), Some(_guard))
}
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
_ => return Err(de::Error::custom("bad enum value")),
};
visitor.visit_enum(EnumDeserializer {
variant,
value,
options: self.options,
visited: self.visited,
})
}
#[inline]
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => {
let mut deserializer = VecDeserializer {
vec: [x, y, z],
next: 0,
options: self.options,
visited: self.visited,
};
visitor.visit_seq(&mut deserializer)
}
Value::Table(t) => {
let _guard = RecursionGuard::new(&t, &self.visited);
let len = t.raw_len() as usize;
let mut deserializer = SeqDeserializer {
seq: t.raw_sequence_values(),
options: self.options,
visited: self.visited,
};
let seq = visitor.visit_seq(&mut deserializer)?;
if deserializer.seq.count() == 0 {
Ok(seq)
} else {
Err(de::Error::invalid_length(
len,
&"fewer elements in the table",
))
}
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
)),
}
}
#[inline]
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_seq(visitor)
}
#[inline]
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_seq(visitor)
}
#[inline]
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::Table(t) => {
let _guard = RecursionGuard::new(&t, &self.visited);
let mut deserializer = MapDeserializer {
pairs: t.pairs(),
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 {
Ok(map)
} else {
Err(de::Error::invalid_length(
deserializer.processed + count,
&"fewer elements in the table",
))
}
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
)),
}
}
#[inline]
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_map(visitor)
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf unit unit_struct newtype_struct
identifier ignored_any
}
}
struct SeqDeserializer<'lua> {
seq: TableSequence<'lua, Value<'lua>>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
loop {
match self.seq.next() {
Some(value) => {
let value = value?;
if check_value_if_skip(&value, self.options, &self.visited)? {
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.seq.size_hint() {
(lower, Some(upper)) if lower == upper => Some(upper),
_ => None,
}
}
}
#[cfg(feature = "luau")]
struct VecDeserializer {
vec: [f32; 3],
next: usize,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
#[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.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(3)
}
}
struct MapDeserializer<'lua> {
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
value: Option<Value<'lua>>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
processed: usize,
}
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
type Error = Error;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
loop {
match self.pairs.next() {
Some(item) => {
let (key, value) = item?;
if check_value_if_skip(&key, self.options, &self.visited)?
|| check_value_if_skip(&value, self.options, &self.visited)?
{
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 seed.deserialize(key_de).map(Some);
}
None => return Ok(None),
}
}
}
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value>
where
T: de::DeserializeSeed<'de>,
{
match self.value.take() {
Some(value) => {
let visited = Rc::clone(&self.visited);
seed.deserialize(Deserializer::from_parts(value, self.options, visited))
}
None => Err(de::Error::custom("value is missing")),
}
}
fn size_hint(&self) -> Option<usize> {
match self.pairs.size_hint() {
(lower, Some(upper)) if lower == upper => Some(upper),
_ => None,
}
}
}
struct EnumDeserializer<'lua> {
variant: StdString,
value: Option<Value<'lua>>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
type Error = Error;
type Variant = VariantDeserializer<'lua>;
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,
options: self.options,
visited: self.visited,
};
seed.deserialize(variant).map(|v| (v, variant_access))
}
}
struct VariantDeserializer<'lua> {
value: Option<Value<'lua>>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
type Error = Error;
fn unit_variant(self) -> Result<()> {
match self.value {
Some(_) => Err(de::Error::invalid_type(
de::Unexpected::NewtypeVariant,
&"unit variant",
)),
None => Ok(()),
}
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
where
T: de::DeserializeSeed<'de>,
{
match self.value {
Some(value) => {
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
}
None => Err(de::Error::invalid_type(
de::Unexpected::UnitVariant,
&"newtype variant",
)),
}
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
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",
)),
}
}
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
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",
)),
}
}
}
// Adds `ptr` to the `visited` map and removes on drop
// Used to track recursive tables but allow to traverse same tables multiple times
struct RecursionGuard {
ptr: *const c_void,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl RecursionGuard {
#[inline]
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
let visited = Rc::clone(visited);
let lua = table.0.lua;
let ptr =
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
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
fn check_value_if_skip(
value: &Value,
options: Options,
visited: &RefCell<FxHashSet<*const c_void>>,
) -> Result<bool> {
match value {
Value::Table(table) => {
let lua = table.0.lua;
let ptr =
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
if visited.borrow().contains(&ptr) {
if options.deny_recursive_tables {
return Err(de::Error::custom("recursive table detected"));
}
return Ok(true); // skip
}
}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_)
if !options.deny_unsupported_types =>
{
return Ok(true); // skip
}
_ => {}
}
Ok(false) // do not skip
}
+274
View File
@@ -0,0 +1,274 @@
//! (De)Serialization support using serde.
use std::os::raw::c_void;
use std::ptr;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub trait LuaSerdeExt<'lua> {
/// A special value (lightuserdata) to encode/decode optional (none) values.
///
/// Requires `feature = "serialize"`
///
/// # Example
///
/// ```
/// use std::collections::HashMap;
/// use mlua::{Lua, Result, LuaSerdeExt};
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// lua.globals().set("null", lua.null())?;
///
/// let val = lua.load(r#"{a = null}"#).eval()?;
/// let map: HashMap<String, Option<String>> = lua.from_value(val)?;
/// assert_eq!(map["a"], None);
///
/// Ok(())
/// }
/// ```
fn null(&'lua self) -> Value<'lua>;
/// 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
/// as the `#` operator on that table.
///
/// Requires `feature = "serialize"`
///
/// # Example
///
/// ```
/// use mlua::{Lua, Result, LuaSerdeExt};
/// use serde_json::Value as JsonValue;
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// lua.globals().set("array_mt", lua.array_metatable())?;
///
/// // Encode as an empty array (no sequence part in the lua table)
/// let val = lua.load("setmetatable({a = 5}, array_mt)").eval()?;
/// let j: JsonValue = lua.from_value(val)?;
/// assert_eq!(j.to_string(), "[]");
///
/// // Encode as object
/// let val = lua.load("{a = 5}").eval()?;
/// let j: JsonValue = lua.from_value(val)?;
/// assert_eq!(j.to_string(), r#"{"a":5}"#);
///
/// Ok(())
/// }
/// ```
fn array_metatable(&'lua self) -> Table<'lua>;
/// Converts `T` into a [`Value`] instance.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # Example
///
/// ```
/// use mlua::{Lua, Result, LuaSerdeExt};
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct User {
/// name: String,
/// age: u8,
/// }
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// let u = User {
/// name: "John Smith".into(),
/// age: 20,
/// };
/// lua.globals().set("user", lua.to_value(&u)?)?;
/// lua.load(r#"
/// assert(user["name"] == "John Smith")
/// assert(user["age"] == 20)
/// "#).exec()
/// }
/// ```
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
/// Converts `T` into a [`Value`] instance with options.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # Example
///
/// ```
/// use mlua::{Lua, Result, LuaSerdeExt, SerializeOptions};
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// let v = vec![1, 2, 3];
/// let options = SerializeOptions::new().set_array_metatable(false);
/// lua.globals().set("v", lua.to_value_with(&v, options)?)?;
///
/// lua.load(r#"
/// assert(#v == 3 and v[1] == 1 and v[2] == 2 and v[3] == 3)
/// assert(getmetatable(v) == nil)
/// "#).exec()
/// }
/// ```
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
where
T: Serialize + ?Sized;
/// Deserializes a [`Value`] into any serde deserializable object.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # Example
///
/// ```
/// use mlua::{Lua, Result, LuaSerdeExt};
/// 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}"#).eval()?;
/// let u: User = lua.from_value(val)?;
///
/// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
///
/// Ok(())
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
/// Deserializes a [`Value`] into any serde deserializable object with options.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # 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: Deserialize<'lua>>(
&'lua self,
value: Value<'lua>,
options: de::Options,
) -> Result<T>;
}
impl<'lua> LuaSerdeExt<'lua> for Lua {
fn null(&'lua self) -> Value<'lua> {
Value::LightUserData(LightUserData(ptr::null_mut()))
}
fn array_metatable(&'lua self) -> Table<'lua> {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1);
push_array_metatable(self.state);
Table(self.pop_ref())
}
}
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
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>>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new_with_options(self, options))
}
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
where
T: Deserialize<'lua>,
{
T::deserialize(de::Deserializer::new(value))
}
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
where
T: Deserialize<'lua>,
{
T::deserialize(de::Deserializer::new_with_options(value, options))
}
}
// Uses 2 stack spaces and calls checkstack.
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
check_stack(state, 2)?;
protect_lua!(state, 0, 0, fn(state) {
ffi::lua_createtable(state, 0, 1);
ffi::lua_pushstring(state, cstr!("__metatable"));
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
})
}
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
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);
}
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
pub mod de;
pub mod ser;
#[doc(inline)]
pub use de::Deserializer;
#[doc(inline)]
pub use ser::Serializer;
+485
View File
@@ -0,0 +1,485 @@
use std::os::raw::c_int;
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::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, StackGuard};
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
pub struct Serializer<'lua> {
lua: &'lua Lua,
options: Options,
}
/// A struct with options to change default serializer behavior.
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct Options {
/// If true, sequence serialization to a Lua table will create table
/// with the [`array_metatable`] attached.
///
/// Default: **true**
///
/// [`array_metatable`]: crate::LuaSerdeExt::array_metatable
pub set_array_metatable: bool,
/// If true, serialize `None` (part of the `Option` type) to [`null`].
/// Otherwise it will be set to Lua [`Nil`].
///
/// Default: **true**
///
/// [`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`].
/// Otherwise it will be set to Lua [`Nil`].
///
/// Default: **true**
///
/// [`null`]: crate::LuaSerdeExt::null
/// [`Nil`]: crate::Value::Nil
pub serialize_unit_to_null: bool,
}
impl Default for Options {
fn default() -> Self {
Self::new()
}
}
impl Options {
/// 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,
}
}
/// Sets [`set_array_metatable`] option.
///
/// [`set_array_metatable`]: #structfield.set_array_metatable
#[must_use]
pub const fn set_array_metatable(mut self, enabled: bool) -> Self {
self.set_array_metatable = enabled;
self
}
/// Sets [`serialize_none_to_null`] option.
///
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
#[must_use]
pub const fn serialize_none_to_null(mut self, enabled: bool) -> Self {
self.serialize_none_to_null = enabled;
self
}
/// Sets [`serialize_unit_to_null`] option.
///
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
#[must_use]
pub const fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
self.serialize_unit_to_null = enabled;
self
}
}
impl<'lua> Serializer<'lua> {
/// Creates a new Lua Serializer with default options.
pub fn new(lua: &'lua 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 {
Serializer { lua, options }
}
}
macro_rules! lua_serialize_number {
($name:ident, $t:ty) => {
#[inline]
fn $name(self, value: $t) -> Result<Value<'lua>> {
value.to_lua(self.lua)
}
};
}
impl<'lua> ser::Serializer for Serializer<'lua> {
type Ok = Value<'lua>;
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>;
#[inline]
fn serialize_bool(self, value: bool) -> Result<Value<'lua>> {
Ok(Value::Boolean(value))
}
lua_serialize_number!(serialize_i8, i8);
lua_serialize_number!(serialize_u8, u8);
lua_serialize_number!(serialize_i16, i16);
lua_serialize_number!(serialize_u16, u16);
lua_serialize_number!(serialize_i32, i32);
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>> {
self.serialize_str(&value.to_string())
}
#[inline]
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
self.lua.create_string(value).map(Value::String)
}
#[inline]
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
self.lua.create_string(value).map(Value::String)
}
#[inline]
fn serialize_none(self) -> Result<Value<'lua>> {
if self.options.serialize_none_to_null {
Ok(self.lua.null())
} else {
Ok(Value::Nil)
}
}
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
where
T: Serialize + ?Sized,
{
value.serialize(self)
}
#[inline]
fn serialize_unit(self) -> Result<Value<'lua>> {
if self.options.serialize_unit_to_null {
Ok(self.lua.null())
} else {
Ok(Value::Nil)
}
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
if self.options.serialize_unit_to_null {
Ok(self.lua.null())
} else {
Ok(Value::Nil)
}
}
#[inline]
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Value<'lua>> {
self.serialize_str(variant)
}
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
where
T: Serialize + ?Sized,
{
value.serialize(self)
}
#[inline]
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Value<'lua>>
where
T: Serialize + ?Sized,
{
let table = self.lua.create_table()?;
let variant = self.lua.create_string(variant)?;
let value = self.lua.to_value_with(value, self.options)?;
table.raw_set(variant, value)?;
Ok(Value::Table(table))
}
#[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)?;
if self.options.set_array_metatable {
table.set_metatable(Some(self.lua.array_metatable()));
}
let options = self.options;
Ok(SerializeVec { table, options })
}
#[inline]
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
self.serialize_seq(Some(len))
}
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
self.serialize_seq(Some(len))
}
#[inline]
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant> {
Ok(SerializeTupleVariant {
name: self.lua.create_string(variant)?,
table: self.lua.create_table()?,
options: self.options,
})
}
#[inline]
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
let len = len.unwrap_or(0) as c_int;
Ok(SerializeMap {
key: None,
table: self.lua.create_table_with_capacity(0, len)?,
options: self.options,
})
}
#[inline]
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
self.serialize_map(Some(len))
}
#[inline]
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
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)?,
options: self.options,
})
}
}
#[doc(hidden)]
pub struct SerializeVec<'lua> {
table: Table<'lua>,
options: Options,
}
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
type Ok = Value<'lua>;
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, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
fn end(self) -> Result<Value<'lua>> {
Ok(Value::Table(self.table))
}
}
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
type Ok = Value<'lua>;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
ser::SerializeSeq::end(self)
}
}
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
type Ok = Value<'lua>;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
ser::SerializeSeq::end(self)
}
}
#[doc(hidden)]
pub struct SerializeTupleVariant<'lua> {
name: String<'lua>,
table: Table<'lua>,
options: Options,
}
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
type Ok = Value<'lua>;
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)?)
}
fn end(self) -> Result<Value<'lua>> {
let lua = self.table.0.lua;
let table = lua.create_table()?;
table.raw_set(self.name, self.table)?;
Ok(Value::Table(table))
}
}
#[doc(hidden)]
pub struct SerializeMap<'lua> {
table: Table<'lua>,
key: Option<Value<'lua>>,
options: Options,
}
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
type Ok = Value<'lua>;
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)?);
Ok(())
}
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
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)?;
self.table.raw_set(key, value)
}
fn end(self) -> Result<Value<'lua>> {
Ok(Value::Table(self.table))
}
}
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
type Ok = Value<'lua>;
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)
}
fn end(self) -> Result<Value<'lua>> {
ser::SerializeMap::end(self)
}
}
#[doc(hidden)]
pub struct SerializeStructVariant<'lua> {
name: String<'lua>,
table: Table<'lua>,
options: Options,
}
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
type Ok = Value<'lua>;
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)?)?;
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)?;
Ok(Value::Table(table))
}
}
+113
View File
@@ -0,0 +1,113 @@
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.4/manual.html#6.2) library
///
/// 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.4/manual.html#6.6) library
pub const TABLE: StdLib = StdLib(1 << 1);
/// [`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.4/manual.html#6.9) library
pub const OS: StdLib = StdLib(1 << 3);
/// [`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.4/manual.html#6.5) library
///
/// 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/luau"`
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
pub const BIT: StdLib = StdLib(1 << 6);
/// [`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.4/manual.html#6.3) library
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub const PACKAGE: StdLib = StdLib(1 << 8);
/// [`jit`](http://luajit.org/ext_jit.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const JIT: StdLib = StdLib(1 << 9);
/// (**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.4/manual.html#6.10) library
pub const DEBUG: StdLib = StdLib(1 << 31);
/// No libraries
pub const NONE: StdLib = StdLib(0);
/// (**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
}
}
impl BitAnd for StdLib {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
StdLib(self.0 & rhs.0)
}
}
impl BitAndAssign for StdLib {
fn bitand_assign(&mut self, rhs: Self) {
*self = StdLib(self.0 & rhs.0)
}
}
impl BitOr for StdLib {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
StdLib(self.0 | rhs.0)
}
}
impl BitOrAssign for StdLib {
fn bitor_assign(&mut self, rhs: Self) {
*self = StdLib(self.0 | rhs.0)
}
}
impl BitXor for StdLib {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self::Output {
StdLib(self.0 ^ rhs.0)
}
}
impl BitXorAssign for StdLib {
fn bitxor_assign(&mut self, rhs: Self) {
*self = StdLib(self.0 ^ rhs.0)
}
}
+58
View File
@@ -1,5 +1,14 @@
use std::borrow::{Borrow, Cow};
use std::hash::{Hash, Hasher};
use std::string::String as StdString;
use std::{slice, str};
#[cfg(feature = "serialize")]
use {
serde::ser::{Serialize, Serializer},
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
@@ -38,6 +47,28 @@ impl<'lua> String<'lua> {
})
}
/// Converts this string to a [`Cow<str>`].
///
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
///
/// # Examples
///
/// ```
/// # use mlua::{Lua, Result};
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
///
/// let s = lua.create_string(b"test\xff")?;
/// assert_eq!(s.to_string_lossy(), "test\u{fffd}");
/// # Ok(())
/// # }
/// ```
pub fn to_string_lossy(&self) -> Cow<'_, str> {
StdString::from_utf8_lossy(self.as_bytes())
}
/// Get the bytes that make up this string.
///
/// The returned slice will not contain the terminating nul byte, but will contain any nul
@@ -89,6 +120,12 @@ impl<'lua> AsRef<[u8]> for String<'lua> {
}
}
impl<'lua> Borrow<[u8]> for String<'lua> {
fn borrow(&self) -> &[u8] {
self.as_bytes()
}
}
// 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`
@@ -105,3 +142,24 @@ where
self.as_bytes() == other.as_ref()
}
}
impl<'lua> Eq for String<'lua> {}
impl<'lua> Hash for String<'lua> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for String<'lua> {
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()),
}
}
}
+481 -120
View File
@@ -1,17 +1,27 @@
use std::marker::PhantomData;
use std::os::raw::c_int;
use crate::error::Result;
#[cfg(feature = "serialize")]
use {
rustc_hash::FxHashSet,
serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
std::{cell::RefCell, os::raw::c_void, result::Result as StdResult},
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::types::{Integer, LuaRef};
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// Handle to an internal Lua table.
#[derive(Clone, Debug)]
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
#[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> {
/// Sets a key-value pair in the table.
///
@@ -50,19 +60,15 @@ impl<'lua> Table<'lua> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_settable(state, -3);
1
}
protect_lua(lua.state, 3, set_table)
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
}
}
@@ -93,18 +99,15 @@ impl<'lua> Table<'lua> {
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
let value = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettable(state, -2);
1
}
protect_lua(lua.state, 2, get_table)?;
lua.pop_value()
};
V::from_lua(value, lua)
@@ -117,60 +120,69 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettable(state, -2);
1
}
protect_lua(lua.state, 2, get_table)?;
let has = ffi::lua_isnil(lua.state, -1) == 0;
Ok(has)
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
Ok(ffi::lua_isnil(lua.state, -1) == 0)
}
}
/// Gets the function associated to `key` from the table and executes it,
/// passing the table itself as the first argument.
/// Compares two tables for equality.
///
/// Tables are compared by reference first.
/// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
/// mlua will check `self` first for the metamethod, then `other` if not found.
///
/// # Examples
///
/// Execute the table method with name "concat":
/// Compare two tables using `__eq` metamethod:
///
/// ```
/// # use mlua::{Lua, Result, Table};
/// # fn main() -> Result<()> {
/// # let lua = Lua::new();
/// # let object = lua.create_table()?;
/// # let concat = lua.create_function(|_, (_, a, b): (Table, String, String)| Ok(a + &b))?;
/// # object.set("concat", concat)?;
/// // simiar to: object:concat("param1", "param2")
/// object.call("concat", ("param1", "param2"))?;
/// let table1 = lua.create_table()?;
/// table1.set(1, "value")?;
///
/// let table2 = lua.create_table()?;
/// table2.set(2, "value")?;
///
/// let always_equals_mt = lua.create_table()?;
/// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
/// table2.set_metatable(Some(always_equals_mt));
///
/// assert!(table1.equals(&table1.clone())?);
/// assert!(table1.equals(&table2)?);
/// # Ok(())
/// # }
/// ```
///
/// This might invoke the `__index` metamethod.
pub fn call<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let mut args = args.to_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(key)?.call(args)
}
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
if self == other {
return Ok(true);
}
/// Removes a key from the table, returning the value at the key
/// if the key was previously in the table.
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
self.raw_set(key, Nil)?;
Ok(())
// Compare using __eq metamethod if exists
// First, check the self for the metamethod.
// If self does not define it, then check the other table.
if let Some(mt) = self.get_metatable() {
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
}
}
if let Some(mt) = other.get_metatable() {
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
}
}
Ok(false)
}
/// Sets a key-value pair without invoking metamethods.
@@ -181,19 +193,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, -3);
0
}
protect_lua(lua.state, 3, raw_set)?;
Ok(())
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
}
}
@@ -201,18 +206,82 @@ impl<'lua> Table<'lua> {
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
let value = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
ffi::lua_rawget(lua.state, -2);
lua.pop_value()
};
V::from_lua(value, lua)
}
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
/// The worst case complexity is O(n), where n is the table length.
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
let lua = self.0.lua;
let size = self.raw_len();
if idx < 1 || idx > size + 1 {
return Err(Error::RuntimeError("index out of bounds".to_string()));
}
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
protect_lua!(lua.state, 2, 0, |state| {
for i in (idx..=size).rev() {
// table[i+1] = table[i]
ffi::lua_rawgeti(state, -2, i);
ffi::lua_rawseti(state, -3, i + 1);
}
ffi::lua_rawseti(state, -2, idx)
})
}
}
/// Removes a key from the table.
///
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
/// where n is the table length.
///
/// For other key types this is equivalent to setting `table[key] = nil`.
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
match key {
Value::Integer(idx) => {
let size = self.raw_len();
if idx < 1 || idx > size {
return Err(Error::RuntimeError("index out of bounds".to_string()));
}
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua!(lua.state, 1, 0, |state| {
for i in idx..size {
ffi::lua_rawgeti(state, -1, i + 1);
ffi::lua_rawseti(state, -2, i);
}
ffi::lua_pushnil(state);
ffi::lua_rawseti(state, -2, size);
})
}
}
_ => self.raw_set(key, Nil),
}
}
/// Returns the result of the Lua `#` operator.
///
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
@@ -222,9 +291,10 @@ impl<'lua> Table<'lua> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua_closure(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
}
}
@@ -234,9 +304,9 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let len = ffi::lua_rawlen(lua.state, -1);
len as Integer
ffi::lua_rawlen(lua.state, -1) as Integer
}
}
@@ -247,13 +317,13 @@ impl<'lua> Table<'lua> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
assert_stack(lua.state, 2);
lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 {
None
} else {
let table = Table(lua.pop_ref());
Some(table)
Some(Table(lua.pop_ref()))
}
}
}
@@ -266,7 +336,8 @@ impl<'lua> Table<'lua> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
assert_stack(lua.state, 2);
lua.push_ref(&self.0);
if let Some(metatable) = metatable {
lua.push_ref(&metatable.0);
@@ -277,6 +348,34 @@ impl<'lua> Table<'lua> {
}
}
/// Sets `readonly` attribute on the table.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_readonly(&self, enabled: bool) {
let lua = self.0.lua;
unsafe {
lua.ref_thread_exec(|refthr| {
ffi::lua_setreadonly(refthr, self.0.index, enabled as _);
if !enabled {
// Reset "safeenv" flag
ffi::lua_setsafeenv(refthr, self.0.index, 0);
}
});
}
}
/// Returns `readonly` attribute of the table.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn is_readonly(&self) -> bool {
let lua = self.0.lua;
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_getreadonly(refthr, self.0.index) != 0) }
}
/// Consume this table and return an iterator over the pairs of the table.
///
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
@@ -308,12 +407,12 @@ impl<'lua> Table<'lua> {
/// # }
/// ```
///
/// [`Result`]: type.Result.html
/// [Lua manual]: http://www.lua.org/manual/5.3/manual.html#pdf-next
/// [`Result`]: crate::Result
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
TablePairs {
table: self.0,
next_key: Some(Nil),
key: Some(Nil),
_phantom: PhantomData,
}
}
@@ -321,7 +420,7 @@ impl<'lua> Table<'lua> {
/// Consume this table and return an iterator over all values in the sequence part of the table.
///
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
/// encountered. This mirrors the behaviour of Lua's `ipairs` function and will invoke the
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
/// metatable will not be called.
///
@@ -357,25 +456,286 @@ impl<'lua> Table<'lua> {
/// ```
///
/// [`pairs`]: #method.pairs
/// [`Result`]: type.Result.html
/// [Lua manual]: http://www.lua.org/manual/5.3/manual.html#pdf-next
/// [`Result`]: crate::Result
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
TableSequence {
table: self.0,
index: Some(1),
len: None,
raw: false,
_phantom: PhantomData,
}
}
/// Consume this table and return an iterator over all values in the sequence part of the table.
///
/// Unlike the `sequence_values`, does not invoke `__index` metamethod when iterating.
///
/// [`sequence_values`]: #method.sequence_values
pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
TableSequence {
table: self.0,
index: Some(1),
len: None,
raw: true,
_phantom: PhantomData,
}
}
#[cfg(any(feature = "serialize"))]
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
self,
len: Option<Integer>,
) -> TableSequence<'lua, V> {
let len = len.unwrap_or_else(|| self.raw_len());
TableSequence {
table: self.0,
index: Some(1),
len: Some(len),
raw: true,
_phantom: PhantomData,
}
}
#[cfg(feature = "serialize")]
pub(crate) fn is_array(&self) -> bool {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return false;
}
crate::serde::push_array_metatable(lua.state);
ffi::lua_rawequal(lua.state, -1, -2) != 0
}
}
}
impl<'lua> PartialEq for Table<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
/// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt<'lua> {
/// Calls the table as function assuming it has `__call` metamethod.
///
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Asynchronously calls the table as function assuming it has `__call` metamethod.
///
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Gets the function associated to `key` from the table and executes it,
/// passing the table itself along with `args` as function arguments.
///
/// This is a shortcut for
/// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
///
/// This might invoke the `__index` metamethod.
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
///
/// This is a shortcut for
/// `table.get::<_, Function>(key)?.call(args)`
///
/// This might invoke the `__index` metamethod.
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing the table itself along with `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<'fut, K, A, R>(
&self,
key: K,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
}
impl<'lua> TableExt<'lua> for Table<'lua> {
fn call<A, R>(&self, args: A) -> Result<R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
// Convert table to a function and call via pcall that respects the `__call` metamethod.
Function(self.0.clone()).call(args)
}
#[cfg(feature = "async")]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
Function(self.0.clone()).call_async(args)
}
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let mut args = args.to_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(key)?.call(args)
}
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.get::<_, Function>(key)?.call(args)
}
#[cfg(feature = "async")]
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
let mut args = match args.to_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::Table(self.clone()));
self.call_async_function(key, args)
}
#[cfg(feature = "async")]
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
match self.get::<_, Function>(key) {
Ok(func) => func.call_async(args),
Err(e) => Box::pin(future::err(e)),
}
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for Table<'lua> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
thread_local! {
static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
}
let lua = self.0.lua;
let ptr = unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) };
let res = VISITED.with(|visited| {
{
let mut visited = visited.borrow_mut();
if visited.contains(&ptr) {
return Err(ser::Error::custom("recursive table detected"));
}
visited.insert(ptr);
}
let len = self.raw_len() as usize;
if len > 0 || self.is_array() {
let mut seq = serializer.serialize_seq(Some(len))?;
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
let v = v.map_err(serde::ser::Error::custom)?;
seq.serialize_element(&v)?;
}
return seq.end();
}
let mut map = serializer.serialize_map(None)?;
for kv in self.clone().pairs::<Value, Value>() {
let (k, v) = kv.map_err(serde::ser::Error::custom)?;
map.serialize_entry(&k, &v)?;
}
map.end()
});
VISITED.with(|visited| {
visited.borrow_mut().remove(&ptr);
});
res
}
}
/// An iterator over the pairs of a Lua table.
///
/// This struct is created by the [`Table::pairs`] method.
///
/// [`Table::pairs`]: struct.Table.html#method.pairs
/// [`Table::pairs`]: crate::Table::pairs
pub struct TablePairs<'lua, K, V> {
table: LuaRef<'lua>,
next_key: Option<Value<'lua>>,
key: Option<Value<'lua>>,
_phantom: PhantomData<(K, V)>,
}
@@ -387,40 +747,37 @@ where
type Item = Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next_key) = self.next_key.take() {
if let Some(prev_key) = self.key.take() {
let lua = self.table.lua;
let res = (|| {
let res = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
let res = (|| unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.table);
lua.push_value(next_key)?;
lua.push_ref(&self.table);
lua.push_value(prev_key)?;
if protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2) != 0
})? {
ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value();
let value = lua.pop_value();
self.next_key = Some(lua.pop_value());
Some((key, value))
} else {
None
}
};
Ok(if let Some((key, value)) = res {
Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2)
})?;
if next != 0 {
let value = lua.pop_value();
let key = lua.pop_value();
Ok(Some((
key.clone(),
K::from_lua(key, lua)?,
V::from_lua(value, lua)?,
)))
} else {
None
})
Ok(None)
}
})();
match res {
Ok(Some((key, value))) => Some(Ok((key, value))),
Ok(Some((key, ret_key, value))) => {
self.key = Some(key);
Some(Ok((ret_key, value)))
}
Ok(None) => None,
Err(e) => Some(Err(e)),
}
@@ -434,10 +791,12 @@ where
///
/// This struct is created by the [`Table::sequence_values`] method.
///
/// [`Table::sequence_values`]: struct.Table.html#method.sequence_values
/// [`Table::sequence_values`]: crate::Table::sequence_values
pub struct TableSequence<'lua, V> {
table: LuaRef<'lua>,
index: Option<Integer>,
len: Option<Integer>,
raw: bool,
_phantom: PhantomData<V>,
}
@@ -451,27 +810,29 @@ where
if let Some(index) = self.index.take() {
let lua = self.table.lua;
let res = unsafe {
let res = (|| unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?;
lua.push_ref(&self.table);
match protect_lua_closure(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))
{
Ok(ffi::LUA_TNIL) => None,
Ok(_) => {
let value = lua.pop_value();
self.index = Some(index + 1);
Some(Ok(value))
}
Err(err) => Some(Err(err)),
let res = if self.raw {
ffi::lua_rawgeti(lua.state, -1, index)
} else {
protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
};
match res {
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
_ => Ok(Some((index, lua.pop_value()))),
}
};
})();
match res {
Some(Ok(r)) => Some(V::from_lua(r, lua)),
Some(Err(err)) => Some(Err(err)),
None => None,
Ok(Some((index, r))) => {
self.index = Some(index + 1);
Some(V::from_lua(r, lua))
}
Ok(None) => None,
Err(err) => Some(Err(err)),
}
} else {
None
+352 -29
View File
@@ -1,12 +1,33 @@
use std::cmp;
use std::os::raw::c_int;
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_closure, StackGuard,
use crate::util::{check_stack, error_traceback, pop_error, StackGuard};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(any(
feature = "lua54",
all(feature = "luajit", feature = "vendored"),
feature = "luau",
))]
use crate::function::Function;
#[cfg(feature = "async")]
use {
crate::{
lua::{Lua, ASYNC_POLL_PENDING},
value::{MultiValue, Value},
},
futures_core::{future::Future, stream::Stream},
std::{
cell::RefCell,
marker::PhantomData,
pin::Pin,
task::{Context, Poll, Waker},
},
};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
/// Status of a Lua thread (or coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -15,7 +36,7 @@ pub enum ThreadStatus {
///
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
///
/// [`Thread::resume`]: struct.Thread.html#method.resume
/// [`Thread::resume`]: crate::Thread::resume
Resumable,
/// Either the thread has finished executing, or the thread is currently running.
Unresumable,
@@ -27,6 +48,22 @@ pub enum ThreadStatus {
#[derive(Clone, Debug)]
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
///
/// Requires `feature = "async"`
///
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::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>>>>,
ret: PhantomData<R>,
recycle: bool,
}
impl<'lua> Thread<'lua> {
/// Resumes execution of this thread.
///
@@ -75,44 +112,38 @@ impl<'lua> Thread<'lua> {
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let args = args.to_lua_multi(lua)?;
let mut args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
let thread_state =
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
return Err(Error::CoroutineInactive);
}
ffi::lua_pop(lua.state, 1);
let nargs = args.len() as c_int;
check_stack(lua.state, nargs)?;
check_stack(thread_state, nargs + 1)?;
for arg in args {
check_stack(thread_state, nargs)?;
for arg in args.drain_all() {
lua.push_value(arg)?;
}
ffi::lua_xmove(lua.state, thread_state, nargs);
let ret = ffi::lua_resume(thread_state, lua.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_closure(lua.state, 0, 0, |_| {
error_traceback(thread_state);
0
})?;
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
return Err(pop_error(thread_state, ret));
}
let nresults = ffi::lua_gettop(thread_state);
let mut results = MultiValue::new();
let mut results = args; // Reuse MultiValue container
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
ffi::lua_xmove(thread_state, lua.state, nresults);
assert_stack(lua.state, 2);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
@@ -125,12 +156,8 @@ impl<'lua> Thread<'lua> {
pub fn status(&self) -> ThreadStatus {
let lua = self.0.lua;
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 thread_state =
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
@@ -142,4 +169,300 @@ impl<'lua> Thread<'lua> {
}
}
}
/// Resets a thread
///
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
/// Returns a error in case of either the original error that stopped the thread or errors
/// in closing methods.
///
/// In [LuaJIT] and 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.
///
/// Sets a Lua function for the thread afterwards.
///
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"` OR `feature = "luau"`
///
/// [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"),
feature = "luau",
))]
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
#[cfg(feature = "lua54")]
let status = ffi::lua_resetthread(thread_state);
#[cfg(feature = "lua54")]
if status != ffi::LUA_OK {
return Err(pop_error(thread_state, status));
}
#[cfg(all(feature = "luajit", feature = "vendored"))]
ffi::lua_resetthread(lua.state, thread_state);
#[cfg(feature = "luau")]
ffi::lua_resetthread(thread_state);
lua.push_ref(&func.0);
ffi::lua_xmove(lua.state, thread_state, 1);
#[cfg(feature = "luau")]
{
// Inherit `LUA_GLOBALSINDEX` from the caller
ffi::lua_xpush(lua.state, thread_state, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
}
Ok(())
}
}
/// 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 calls [`resume()`] while polling and also allows 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
/// one (returned from the thread function).
///
/// Requires `feature = "async"`
///
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
///
/// # Examples
///
/// ```
/// # use mlua::{Lua, Result, Thread};
/// use futures::stream::TryStreamExt;
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
/// let thread: Thread = lua.load(r#"
/// coroutine.create(function (sum)
/// for i = 1,10 do
/// sum = sum + i
/// coroutine.yield(sum)
/// end
/// return sum
/// end)
/// "#).eval()?;
///
/// let mut stream = thread.into_async::<_, i64>(1);
/// let mut sum = 0;
/// while let Some(n) = stream.try_next().await? {
/// sum += n;
/// }
///
/// assert_eq!(sum, 286);
///
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let args = args.to_lua_multi(self.0.lua);
AsyncThread {
thread: self,
args0: RefCell::new(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};
/// # 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(())
/// # }
/// ```
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
let lua = self.0.lua;
unsafe {
let thread = lua.ref_thread_exec(|t| ffi::lua_tothread(t, self.0.index));
check_stack(thread, 1)?;
check_stack(lua.state, 3)?;
// Inherit `LUA_GLOBALSINDEX` from the caller
ffi::lua_xpush(lua.state, thread, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
}
}
}
impl<'lua> PartialEq for Thread<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
#[cfg(feature = "async")]
impl<'lua, R> AsyncThread<'lua, R> {
#[inline]
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
self.recycle = recyclable;
}
}
#[cfg(feature = "async")]
#[cfg(any(
feature = "lua54",
all(feature = "luajit", feature = "vendored"),
feature = "luau",
))]
impl<'lua, R> Drop for AsyncThread<'lua, R> {
fn drop(&mut self) {
if self.recycle {
unsafe {
self.thread.0.lua.recycle_thread(&mut self.thread);
}
}
}
}
#[cfg(feature = "async")]
impl<'lua, R> Stream for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
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, 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;
}
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
}
}
#[cfg(feature = "async")]
impl<'lua, R> Future for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
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, 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;
}
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")]
#[inline(always)]
fn is_poll_pending(val: &MultiValue) -> bool {
match val.iter().enumerate().last() {
Some((0, Value::LightUserData(ud))) => {
std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8)
}
_ => false,
}
}
#[cfg(feature = "async")]
struct WakerGuard<'lua> {
lua: &'lua Lua,
prev: Option<Waker>,
}
#[cfg(feature = "async")]
impl<'lua> WakerGuard<'lua> {
#[inline]
pub fn new(lua: &Lua, waker: Waker) -> Result<WakerGuard> {
unsafe {
let prev = lua.set_waker(Some(waker));
Ok(WakerGuard { lua, prev })
}
}
}
#[cfg(feature = "async")]
impl<'lua> Drop for WakerGuard<'lua> {
fn drop(&mut self) {
unsafe {
self.lua.set_waker(self.prev.take());
}
}
}
+111 -18
View File
@@ -1,10 +1,21 @@
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr};
#[cfg(feature = "lua54")]
use std::ffi::CStr;
#[cfg(feature = "async")]
use futures_core::future::LocalBoxFuture;
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::lua::{ExtraData, Lua};
use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue;
/// Type of Lua integer numbers.
@@ -19,26 +30,78 @@ pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: Arc<UnsafeCell<ExtraData>>,
}
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<MultiValue<'static>>>>;
/// Type to set next Luau VM action after executing interrupt function.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub enum VmState {
Continue,
Yield,
}
#[cfg(all(feature = "send", not(feature = "luau")))]
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
#[cfg(all(feature = "luau", feature = "send"))]
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState> + Send>;
#[cfg(all(feature = "luau", not(feature = "send")))]
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
#[cfg(all(not(feature = "send"), feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()>>;
#[cfg(feature = "send")]
pub trait MaybeSend: Send {}
#[cfg(feature = "send")]
impl<T: Send> MaybeSend for T {}
#[cfg(not(feature = "send"))]
pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
pub(crate) struct DestructedUserdataMT;
/// An auto generated key into the Lua registry.
///
/// This is a handle to a value stored inside the Lua registry. It is not directly usable like the
/// `Table` or `Function` handle types, but since it doesn't hold a reference to a parent Lua and is
/// Send + Sync + 'static, it is much more flexible and can be used in many situations where it is
/// impossible to directly store a normal handle type. It is not automatically garbage collected on
/// Drop, but it can be removed with [`Lua::remove_registry_value`], and instances not manually
/// removed can be garbage collected with [`Lua::expire_registry_values`].
/// This is a handle to a value stored inside the Lua registry. It is not automatically
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
///
/// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
/// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use
/// [`UserData::set_user_value`] / [`UserData::get_user_value`], and instead of moving a RegistryKey
/// into a callback, prefer [`Lua::scope`].
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
/// [`AnyUserData::set_user_value`] / [`AnyUserData::get_user_value`].
///
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
/// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`UserData::set_user_value`]: struct.UserData.html#method.set_user_value
/// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value
/// [`UserData`]: crate::UserData
/// [`RegistryKey`]: crate::RegistryKey
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
pub struct RegistryKey {
pub(crate) registry_id: c_int,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
@@ -50,9 +113,24 @@ impl fmt::Debug for RegistryKey {
}
}
impl Hash for RegistryKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.registry_id.hash(state)
}
}
impl PartialEq for RegistryKey {
fn eq(&self, other: &RegistryKey) -> bool {
self.registry_id == other.registry_id && Arc::ptr_eq(&self.unref_list, &other.unref_list)
}
}
impl Eq for RegistryKey {}
impl Drop for RegistryKey {
fn drop(&mut self) {
if let Some(list) = mlua_expect!(self.unref_list.lock(), "unref_list poisoned").as_mut() {
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
if let Some(list) = unref_list.as_mut() {
list.push(self.registry_id);
}
}
@@ -89,6 +167,21 @@ impl<'lua> Clone for LuaRef<'lua> {
impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) {
self.lua.drop_ref(self)
if self.index > 0 {
self.lua.drop_ref(self);
}
}
}
impl<'lua> PartialEq for LuaRef<'lua> {
fn eq(&self, other: &Self) -> bool {
let lua = self.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(self);
lua.push_ref(other);
ffi::lua_rawequal(lua.state, -1, -2) == 1
}
}
}
+947 -141
View File
File diff suppressed because it is too large Load Diff
+624
View File
@@ -0,0 +1,624 @@
use std::any::TypeId;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::sync::{Arc, Mutex, RwLock};
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::types::{Callback, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{check_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
#[cfg(not(feature = "send"))]
use std::rc::Rc;
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
futures_core::future::Future,
futures_util::future::{self, TryFutureExt},
};
pub(crate) struct StaticUserDataMethods<'lua, T: 'static + UserData> {
pub(crate) methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
pub(crate) async_methods: Vec<(Vec<u8>, AsyncCallback<'lua, 'static>)>,
pub(crate) meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(MetaMethod, AsyncCallback<'lua, 'static>)>,
_type: PhantomData<T>,
}
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
fn default() -> StaticUserDataMethods<'lua, T> {
StaticUserDataMethods {
methods: Vec::new(),
#[cfg(feature = "async")]
async_methods: Vec::new(),
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
_type: PhantomData,
}
}
}
impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_method(method)));
}
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_method_mut(method)));
}
#[cfg(feature = "async")]
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
where
T: Clone,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
self.async_methods
.push((name.as_ref().to_vec(), Self::box_async_method(method)));
}
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_function(function)));
}
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_function_mut(function)));
}
#[cfg(feature = "async")]
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
self.async_methods
.push((name.as_ref().to_vec(), Self::box_async_function(function)));
}
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_method(method)));
}
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, method: M)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_method_mut(method)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<S, A, R, M, MR>(&mut self, meta: S, method: M)
where
T: Clone,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
self.async_meta_methods
.push((meta.into(), Self::box_async_method(method)));
}
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_function(function)));
}
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_function_mut(function)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_function<S, A, R, F, FR>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
self.async_meta_methods
.push((meta.into(), Self::box_async_function(function)));
}
// Below are internal methods used in generated code
fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_callback(&mut self, name: Vec<u8>, callback: AsyncCallback<'lua, 'static>) {
self.async_methods.push((name, callback));
}
fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) {
self.meta_methods.push((meta, callback));
}
#[cfg(feature = "async")]
fn add_async_meta_callback(
&mut self,
meta: MetaMethod,
callback: AsyncCallback<'lua, 'static>,
) {
self.async_meta_methods.push((meta, callback))
}
}
impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
fn box_method<A, R, M>(method: M) -> Callback<'lua, 'static>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
Box::new(move |lua, mut args| {
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = get_userdata_ref::<T>(lua.state)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
Err(Error::FromLuaConversionError {
from: "missing argument",
to: "userdata",
message: None,
})
}
})
}
fn box_method_mut<A, R, M>(method: M) -> Callback<'lua, 'static>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
let method = RefCell::new(method);
Box::new(move |lua, mut args| {
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = get_userdata_mut::<T>(lua.state)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = get_userdata_mut::<Rc<RefCell<T>>>(lua.state)?;
let mut ud = ud
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<Mutex<T>>>(lua.state)?;
let mut ud =
ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<RwLock<T>>>(lua.state)?;
let mut ud =
ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
Err(Error::FromLuaConversionError {
from: "missing argument",
to: "userdata",
message: None,
})
}
})
}
#[cfg(feature = "async")]
fn box_async_method<A, R, M, MR>(method: M) -> AsyncCallback<'lua, 'static>
where
T: Clone,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
Box::new(move |lua, mut args| {
let fut_res = || {
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = get_userdata_ref::<T>(lua.state)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud =
get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
Err(Error::FromLuaConversionError {
from: "missing argument",
to: "userdata",
message: None,
})
}
};
match fut_res() {
Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))),
Err(e) => Box::pin(future::err(e)),
}
})
}
fn box_function<A, R, F>(function: F) -> Callback<'lua, 'static>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua))
}
fn box_function_mut<A, R, F>(function: F) -> Callback<'lua, 'static>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
let function = RefCell::new(function);
Box::new(move |lua, args| {
let function = &mut *function
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})
}
#[cfg(feature = "async")]
fn box_async_function<A, R, F, FR>(function: F) -> AsyncCallback<'lua, 'static>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
})
}
}
pub(crate) struct StaticUserDataFields<'lua, T: 'static + UserData> {
pub(crate) field_getters: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
pub(crate) field_setters: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
#[allow(clippy::type_complexity)]
pub(crate) meta_fields: Vec<(
MetaMethod,
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
)>,
_type: PhantomData<T>,
}
impl<'lua, T: 'static + UserData> Default for StaticUserDataFields<'lua, T> {
fn default() -> StaticUserDataFields<'lua, T> {
StaticUserDataFields {
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
_type: PhantomData,
}
}
}
impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFields<'lua, T> {
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
{
self.field_getters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::box_method(move |lua, data, ()| method(lua, data)),
));
}
fn add_field_method_set<S, A, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
{
self.field_setters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::box_method_mut(method),
));
}
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
{
self.field_getters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::<T>::box_function(function),
));
}
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
{
self.field_setters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::<T>::box_function_mut(move |lua, (data, val)| {
function(lua, data, val)
}),
));
}
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
where
S: Into<MetaMethod>,
R: ToLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
{
let meta = meta.into();
self.meta_fields.push((
meta.clone(),
Box::new(move |lua| {
let value = f(lua)?.to_lua(lua)?;
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
match value {
Value::Nil | Value::Table(_) | Value::Function(_) => {}
_ => {
return Err(Error::MetaMethodTypeError {
method: meta.to_string(),
type_name: value.type_name(),
message: Some("expected nil, table or function".to_string()),
})
}
}
}
Ok(value)
}),
));
}
// Below are internal methods
fn add_field_getter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
self.field_getters.push((name, callback));
}
fn add_field_setter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
self.field_setters.push((name, callback));
}
}
#[inline]
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result<Ref<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow()
}
#[inline]
unsafe fn get_userdata_mut<'a, T>(state: *mut ffi::lua_State) -> Result<RefMut<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: 'static + UserData> UserData for $type {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
let mut orig_fields = StaticUserDataFields::default();
T::add_fields(&mut orig_fields);
for (name, callback) in orig_fields.field_getters {
fields.add_field_getter(name, callback);
}
for (name, callback) in orig_fields.field_setters {
fields.add_field_setter(name, callback);
}
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
let mut orig_methods = StaticUserDataMethods::default();
T::add_methods(&mut orig_methods);
for (name, callback) in orig_methods.methods {
methods.add_callback(name, callback);
}
#[cfg(feature = "async")]
for (name, callback) in orig_methods.async_methods {
methods.add_async_callback(name, callback);
}
for (meta, callback) in orig_methods.meta_methods {
methods.add_meta_callback(meta, callback);
}
#[cfg(feature = "async")]
for (meta, callback) in orig_methods.async_meta_methods {
methods.add_async_meta_callback(meta, callback);
}
}
}
};
}
#[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<RefCell<T>>);
lua_userdata_impl!(Arc<Mutex<T>>);
lua_userdata_impl!(Arc<RwLock<T>>);
#[cfg(feature = "parking_lot")]
lua_userdata_impl!(Arc<parking_lot::Mutex<T>>);
#[cfg(feature = "parking_lot")]
lua_userdata_impl!(Arc<parking_lot::RwLock<T>>);
+671 -425
View File
File diff suppressed because it is too large Load Diff
+141 -10
View File
@@ -1,6 +1,13 @@
use std::iter::{self, FromIterator};
use std::{slice, str, vec};
#[cfg(feature = "serialize")]
use {
serde::ser::{self, Serialize, Serializer},
std::convert::TryInto,
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
@@ -10,10 +17,9 @@ use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number};
use crate::userdata::AnyUserData;
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, or between a parent `Lua` instance and one received as a
/// parameter in a Rust callback, and doing so will result in a panic.
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, and doing so will result in a panic.
#[derive(Debug, Clone)]
pub enum Value<'lua> {
/// The Lua value `nil`.
@@ -28,6 +34,10 @@ pub enum Value<'lua> {
Integer(Integer),
/// A floating point number.
Number(Number),
/// A Luau vector.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Vector(f32, f32, f32),
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
@@ -41,24 +51,103 @@ pub enum Value<'lua> {
/// Reference to a userdata object that holds a custom type which implements `UserData`.
/// Special builtin userdata types will be represented as other `Value` variants.
UserData(AnyUserData<'lua>),
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Error),
}
pub use self::Value::Nil;
impl<'lua> Value<'lua> {
pub(crate) fn type_name(&self) -> &'static str {
pub const fn type_name(&self) -> &'static str {
match *self {
Value::Nil => "nil",
Value::Boolean(_) => "boolean",
Value::LightUserData(_) => "light userdata",
Value::LightUserData(_) => "lightuserdata",
Value::Integer(_) => "integer",
Value::Number(_) => "number",
#[cfg(feature = "luau")]
Value::Vector(_, _, _) => "vector",
Value::String(_) => "string",
Value::Table(_) => "table",
Value::Function(_) => "function",
Value::Thread(_) => "thread",
Value::UserData(_) | Value::Error(_) => "userdata",
Value::UserData(_) => "userdata",
Value::Error(_) => "error",
}
}
/// Compares two values for equality.
///
/// Equality comparisons do not convert strings to numbers or vice versa.
/// Tables, Functions, Threads, and Userdata are compared by reference:
/// two objects are considered equal only if they are the same object.
///
/// If Tables or Userdata have `__eq` metamethod then mlua will try to invoke it.
/// The first value is checked first. If that value does not define a metamethod
/// for `__eq`, then mlua will check the second value.
/// Then mlua calls the metamethod with the two values as arguments, if found.
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
match (self, other.as_ref()) {
(Value::Table(a), Value::Table(b)) => a.equals(b),
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
_ => Ok(self == other.as_ref()),
}
}
}
impl<'lua> PartialEq for Value<'lua> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Nil, Value::Nil) => true,
(Value::Boolean(a), Value::Boolean(b)) => a == b,
(Value::LightUserData(a), Value::LightUserData(b)) => a == b,
(Value::Integer(a), Value::Integer(b)) => *a == *b,
(Value::Integer(a), Value::Number(b)) => *a as Number == *b,
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
(Value::Number(a), Value::Number(b)) => *a == *b,
#[cfg(feature = "luau")]
(Value::Vector(x1, y1, z1), Value::Vector(x2, y2, z2)) => (x1, y1, z1) == (x2, y2, z2),
(Value::String(a), Value::String(b)) => a == b,
(Value::Table(a), Value::Table(b)) => a == b,
(Value::Function(a), Value::Function(b)) => a == b,
(Value::Thread(a), Value::Thread(b)) => a == b,
(Value::UserData(a), Value::UserData(b)) => a == b,
_ => false,
}
}
}
impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for Value<'lua> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Nil => serializer.serialize_unit(),
Value::Boolean(b) => serializer.serialize_bool(*b),
#[allow(clippy::useless_conversion)]
Value::Integer(i) => serializer
.serialize_i64((*i).try_into().expect("cannot convert lua_Integer to i64")),
#[allow(clippy::useless_conversion)]
Value::Number(n) => serializer.serialize_f64(*n),
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
Value::String(s) => s.serialize(serializer),
Value::Table(t) => t.serialize(serializer),
Value::UserData(ud) => ud.serialize(serializer),
Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
Value::Error(_) | Value::LightUserData(_) | Value::Function(_) | Value::Thread(_) => {
let msg = format!("cannot serialize <{}>", self.type_name());
Err(ser::Error::custom(msg))
}
}
}
}
@@ -81,18 +170,27 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub fn new() -> MultiValue<'lua> {
MultiValue(Vec::new())
}
/// Similar to `new` but can return previously used container with allocated capacity.
#[inline]
pub(crate) fn new_or_cached(lua: &'lua Lua) -> MultiValue<'lua> {
lua.new_or_cached_multivalue()
}
}
impl<'lua> Default for MultiValue<'lua> {
#[inline]
fn default() -> MultiValue<'lua> {
MultiValue::new()
}
}
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
#[inline]
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue::from_vec(Vec::from_iter(iter))
}
@@ -102,6 +200,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>;
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter().rev()
}
@@ -111,46 +210,78 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type Item = &'a Value<'lua>;
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter().rev()
(&self.0).iter().rev()
}
}
impl<'lua> MultiValue<'lua> {
#[inline]
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
v.reverse();
MultiValue(v)
}
#[inline]
pub fn into_vec(self) -> Vec<Value<'lua>> {
let mut v = self.0;
v.reverse();
v
}
#[inline]
pub(crate) fn reserve(&mut self, size: usize) {
self.0.reserve(size);
}
#[inline]
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
self.0.push(value);
}
#[inline]
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
self.0.pop()
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
self.0.is_empty()
}
#[inline]
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev()
}
#[inline]
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
self.0.drain(..).rev()
}
#[inline]
pub(crate) fn refill(
&mut self,
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
) -> Result<()> {
self.0.clear();
for value in iter {
self.0.push(value?);
}
self.0.reverse();
Ok(())
}
}
/// Trait for types convertible to any number of Lua values.
+488
View File
@@ -0,0 +1,488 @@
#![cfg(feature = "async")]
use std::cell::Cell;
use std::rc::Rc;
use std::sync::{
atomic::{AtomicI64, AtomicU64, Ordering},
Arc,
};
use std::time::Duration;
use futures_timer::Delay;
use futures_util::stream::TryStreamExt;
use mlua::{
Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, Thread, UserData,
UserDataMethods, Value,
};
#[tokio::test]
async fn test_async_function() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
lua.globals().set("f", f)?;
let res: i64 = lua.load("f(1, 2, 3)").eval_async().await?;
assert_eq!(res, 9);
Ok(())
}
#[tokio::test]
async fn test_async_sleep() -> Result<()> {
let lua = Lua::new();
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
lua.globals().set("sleep", sleep)?;
let res: String = lua.load(r"return sleep(...)").call_async(100).await?;
assert_eq!(res, "elapsed:100ms");
Ok(())
}
#[tokio::test]
async fn test_async_call() -> Result<()> {
let lua = Lua::new();
let hello = lua.create_async_function(|_lua, name: String| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(format!("hello, {}!", name))
})?;
match hello.call::<_, ()>("alex") {
Err(Error::RuntimeError(_)) => {}
_ => panic!(
"non-async executing async function must fail on the yield stage with RuntimeError"
),
};
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
// Executing non-async functions using async call is allowed
let sum = lua.create_function(|_lua, (a, b): (i64, i64)| return Ok(a + b))?;
assert_eq!(sum.call_async::<_, i64>((5, 1)).await?, 6);
Ok(())
}
#[tokio::test]
async fn test_async_bind_call() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move { Ok(a + b) })?;
let plus_10 = sum.bind(10)?;
lua.globals().set("plus_10", plus_10)?;
assert_eq!(lua.load("plus_10(-1)").eval_async::<i64>().await?, 9);
assert_eq!(lua.load("plus_10(1)").eval_async::<i64>().await?, 11);
Ok(())
}
#[tokio::test]
async fn test_async_handle_yield() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(a + b)
})?;
lua.globals().set("sleep_sum", sum)?;
let res: String = lua
.load(
r#"
sum = sleep_sum(6, 7)
assert(sum == 13)
coroutine.yield("in progress")
return "done"
"#,
)
.call_async(())
.await?;
assert_eq!(res, "done");
let min = lua
.load(
r#"
function (a, b)
coroutine.yield("ignore me")
if a < b then return a else return b end
end
"#,
)
.eval::<Function>()?;
assert_eq!(min.call_async::<_, i64>((-1, 1)).await?, -1);
Ok(())
}
#[tokio::test]
async fn test_async_multi_return_nil() -> Result<()> {
let lua = Lua::new();
lua.globals().set(
"func",
lua.create_async_function(|_, _: ()| async { Ok((Option::<String>::None, "error")) })?,
)?;
lua.load(
r#"
local ok, err = func()
assert(err == "error")
"#,
)
.exec_async()
.await
}
#[tokio::test]
async fn test_async_return_async_closure() -> Result<()> {
let lua = Lua::new();
let f = lua.create_async_function(|lua, a: i64| async move {
Delay::new(Duration::from_millis(10)).await;
let g = lua.create_async_function(move |_, b: i64| async move {
Delay::new(Duration::from_millis(10)).await;
return Ok(a + b);
})?;
Ok(g)
})?;
lua.globals().set("f", f)?;
let res: i64 = lua
.load("local g = f(1); return g(2) + g(3)")
.call_async(())
.await?;
assert_eq!(res, 7);
Ok(())
}
#[tokio::test]
async fn test_async_thread_stream() -> Result<()> {
let lua = Lua::new();
let thread = lua.create_thread(
lua.load(
r#"
function (sum)
for i = 1,10 do
sum = sum + i
coroutine.yield(sum)
end
return sum
end
"#,
)
.eval()?,
)?;
let mut stream = thread.into_async::<_, i64>(1);
let mut sum = 0;
while let Some(n) = stream.try_next().await? {
sum += n;
}
assert_eq!(sum, 286);
Ok(())
}
#[tokio::test]
async fn test_async_thread() -> Result<()> {
let lua = Lua::new();
let cnt = Arc::new(10); // sleep 10ms
let cnt2 = cnt.clone();
let f = lua.create_async_function(move |_lua, ()| {
let cnt3 = cnt2.clone();
async move {
Delay::new(Duration::from_millis(*cnt3.as_ref())).await;
Ok("done")
}
})?;
let res: String = lua.create_thread(f)?.into_async(()).await?;
assert_eq!(res, "done");
assert_eq!(Arc::strong_count(&cnt), 2);
lua.gc_collect()?; // thread_s is non-resumable and subject to garbage collection
assert_eq!(Arc::strong_count(&cnt), 1);
Ok(())
}
#[tokio::test]
async fn test_async_table() -> Result<()> {
let options = LuaOptions::new().thread_cache_size(4);
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let table = lua.create_table()?;
table.set("val", 10)?;
let get_value = lua.create_async_function(|_, table: Table| async move {
Delay::new(Duration::from_millis(10)).await;
table.get::<_, i64>("val")
})?;
table.set("get_value", get_value)?;
let set_value = lua.create_async_function(|_, (table, n): (Table, i64)| async move {
Delay::new(Duration::from_millis(10)).await;
table.set("val", n)
})?;
table.set("set_value", set_value)?;
let sleep = lua.create_async_function(|_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
})?;
table.set("sleep", sleep)?;
assert_eq!(
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
10
);
table.call_async_method("set_value", 15).await?;
assert_eq!(
table
.call_async_method::<_, _, i64>("get_value", ())
.await?,
15
);
assert_eq!(
table
.call_async_function::<_, _, String>("sleep", 7)
.await?,
"elapsed:7ms"
);
Ok(())
}
#[tokio::test]
async fn test_async_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicU64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(mlua::MetaMethod::Call, |_, data, ()| async move {
let n = data.0.load(Ordering::Relaxed);
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(
mlua::MetaMethod::Index,
|_, data, key: String| async move {
Delay::new(Duration::from_millis(10)).await;
match key.as_str() {
"ms" => Ok(Some(data.0.load(Ordering::Relaxed) as f64)),
"s" => Ok(Some((data.0.load(Ordering::Relaxed) as f64) / 1000.0)),
_ => Ok(None),
}
},
);
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(
mlua::MetaMethod::NewIndex,
|_, data, (key, value): (String, f64)| async move {
Delay::new(Duration::from_millis(10)).await;
match key.as_str() {
"ms" => Ok(data.0.store(value as u64, Ordering::Relaxed)),
"s" => Ok(data.0.store((value * 1000.0) as u64, Ordering::Relaxed)),
_ => Err(Error::external(format!("key '{}' not found", key))),
}
},
);
}
}
let lua = Lua::new();
let globals = lua.globals();
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicU64::new(11))))?;
globals.set("userdata", userdata.clone())?;
lua.load(
r#"
assert(userdata:get_value() == 11)
userdata:set_value(12)
assert(userdata.sleep(5) == "elapsed:5ms")
assert(userdata:get_value() == 12)
"#,
)
.exec_async()
.await?;
#[cfg(not(any(feature = "lua51", feature = "luau")))]
lua.load(
r#"
userdata:set_value(15)
assert(userdata() == "elapsed:15ms")
userdata.ms = 2000
assert(userdata.s == 2)
userdata.s = 15
assert(userdata.ms == 15000)
"#,
)
.exec_async()
.await?;
Ok(())
}
#[tokio::test]
async fn test_async_scope() -> Result<()> {
let ref lua = Lua::new();
let ref rc = Rc::new(Cell::new(0));
let fut = lua.async_scope(|scope| async move {
let f = scope.create_async_function(move |_, n: u64| {
let rc2 = rc.clone();
async move {
rc2.set(42);
Delay::new(Duration::from_millis(n)).await;
assert_eq!(Rc::strong_count(&rc2), 2);
Ok(())
}
})?;
lua.globals().set("f", f.clone())?;
assert_eq!(Rc::strong_count(rc), 1);
let _ = f.call_async::<u64, ()>(10).await?;
assert_eq!(Rc::strong_count(rc), 1);
// Create future in partialy polled state (Poll::Pending)
let g = lua.create_thread(f)?;
g.resume::<u64, ()>(10)?;
lua.globals().set("g", g)?;
assert_eq!(Rc::strong_count(rc), 2);
Ok(())
});
assert_eq!(Rc::strong_count(rc), 1);
let _ = fut.await?;
assert_eq!(Rc::strong_count(rc), 1);
match lua
.globals()
.get::<_, Function>("f")?
.call_async::<_, ()>(10)
.await
{
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
Ok(())
}
#[tokio::test]
async fn test_async_scope_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
}
}
let ref lua = Lua::new();
let ref arc = Arc::new(AtomicI64::new(11));
lua.async_scope(|scope| async move {
let ud = scope.create_userdata(MyUserData(arc.clone()))?;
lua.globals().set("userdata", ud)?;
lua.load(
r#"
assert(userdata:get_value() == 11)
userdata:set_value(12)
assert(userdata.sleep(5) == "elapsed:5ms")
assert(userdata:get_value() == 12)
"#,
)
.exec_async()
.await
})
.await?;
assert_eq!(Arc::strong_count(arc), 1);
match lua.load("userdata:get_value()").exec_async().await {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed userdata: {:?}", r),
};
Ok(())
}
+1 -12
View File
@@ -1,19 +1,8 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use bstr::{BStr, BString};
use mlua::{Lua, Result};
#[test]
fn byte_string_round_trip() -> Result<()> {
fn test_byte_string_round_trip() -> Result<()> {
let lua = Lua::new();
lua.load(
+54
View File
@@ -0,0 +1,54 @@
use std::fs;
use std::io;
use mlua::{Error, Lua, Result};
#[test]
fn test_chunk_path() -> Result<()> {
let lua = Lua::new();
let temp_dir = tempfile::tempdir().unwrap();
fs::write(
temp_dir.path().join("module.lua"),
r#"
return 321
"#,
)?;
let i: i32 = lua.load(&temp_dir.path().join("module.lua")).eval()?;
assert_eq!(i, 321);
match lua.load(&temp_dir.path().join("module2.lua")).exec() {
Err(Error::ExternalError(err))
if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
res => panic!("expected io::Error, got {:?}", res),
};
Ok(())
}
#[test]
#[cfg(feature = "macros")]
fn test_chunk_macro() -> Result<()> {
let lua = Lua::new();
let name = "Rustacean";
let table = vec![1];
let data = lua.create_table()?;
data.raw_set("num", 1)?;
lua.globals().set("g", 123)?;
lua.load(mlua::chunk! {
assert($name == "Rustacean")
assert($table[1] == 1)
assert($data.num == 1)
assert(g == 123)
s = 321
})
.exec()?;
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
Ok(())
}
+24
View File
@@ -0,0 +1,24 @@
#[test]
#[ignore]
fn test_compilation() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/compile/function_borrow.rs");
t.compile_fail("tests/compile/lua_norefunwindsafe.rs");
t.compile_fail("tests/compile/ref_nounwindsafe.rs");
t.compile_fail("tests/compile/scope_callback_capture.rs");
t.compile_fail("tests/compile/scope_callback_inner.rs");
t.compile_fail("tests/compile/scope_callback_outer.rs");
t.compile_fail("tests/compile/scope_invariance.rs");
t.compile_fail("tests/compile/scope_mutable_aliasing.rs");
t.compile_fail("tests/compile/scope_userdata_borrow.rs");
t.compile_fail("tests/compile/static_callback_args.rs");
#[cfg(feature = "async")]
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
#[cfg(feature = "send")]
t.compile_fail("tests/compile/non_send.rs");
#[cfg(not(feature = "send"))]
t.pass("tests/compile/non_send.rs");
}
+17
View File
@@ -0,0 +1,17 @@
use mlua::{Lua, UserData, UserDataMethods};
fn main() {
let ref lua = Lua::new();
#[derive(Clone)]
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("print", |_, data, ()| async move {
println!("{}", data.0);
Ok(())
});
}
}
}
@@ -0,0 +1,41 @@
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
--> tests/compile/async_nonstatic_userdata.rs:9:10
|
9 | impl<'a> UserData for MyUserData<'a> {
| ^^
note: ...so that the types are compatible
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
= note: expected `(MyUserData<'_>,)`
found `(MyUserData<'a>,)`
note: but, the lifetime must be valid for the lifetime `'lua` as defined here...
--> tests/compile/async_nonstatic_userdata.rs:10:24
|
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ^^^^
note: ...so that the type `impl Future<Output = [async output]>` will meet its required lifetime bounds...
--> tests/compile/async_nonstatic_userdata.rs:11:21
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ^^^^^^^^^^^^^^^^
note: ...that is required by this bound
--> src/userdata.rs
|
| MR: 'lua + Future<Output = Result<R>>;
| ^^^^
@@ -1,13 +1,13 @@
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
--> $DIR/function_borrow.rs:9:33
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
10 | Ok(test.0)
| ---- `test` is borrowed here
| ------ `test` is borrowed here
|
note: function requires argument type to outlive `'static`
--> $DIR/function_borrow.rs:9:13
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| _____________^
@@ -17,4 +17,4 @@ note: function requires argument type to outlive `'static`
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ++++
+18
View File
@@ -0,0 +1,18 @@
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
+17
View File
@@ -0,0 +1,17 @@
use std::cell::Cell;
use std::rc::Rc;
use mlua::{Lua, Result};
fn main() -> Result<()> {
let lua = Lua::new();
let data = Rc::new(Cell::new(0));
lua.create_function(move |_, ()| {
Ok(data.get())
})?
.call::<_, i32>(())?;
Ok(())
}
+19
View File
@@ -0,0 +1,19 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> tests/compile/non_send.rs:11:9
|
11 | lua.create_function(move |_, ()| {
| _________^^^^^^^^^^^^^^^_-
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
12 | | Ok(data.get())
13 | | })?
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
|
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+20
View File
@@ -0,0 +1,20 @@
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `LuaTable<'_>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
@@ -1,15 +1,11 @@
use mlua::{Lua, Table, Result};
struct Test {
field: i32,
}
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
lua.scope(|scope| -> Result<()> {
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(move |lua, t: Table| {
.create_function_mut(move |_, t: Table| {
if let Some(old) = inner.take() {
// Access old callback `Lua`.
}
@@ -0,0 +1,26 @@
warning: unused variable: `old`
--> $DIR/scope_callback_capture.rs:9:29
|
9 | if let Some(old) = inner.take() {
| ^^^ help: if this is intentional, prefix it with an underscore: `_old`
|
= note: `#[warn(unused_variables)]` on by default
error[E0521]: borrowed data escapes outside of closure
--> $DIR/scope_callback_capture.rs:7:17
|
5 | lua.scope(|scope| {
| -----
| |
| `scope` declared here, outside of the closure body
| `scope` is a reference that is only valid in the closure body
6 | let mut inner: Option<Table> = None;
7 | let f = scope
| _________________^
8 | | .create_function_mut(move |_, t: Table| {
9 | | if let Some(old) = inner.take() {
10 | | // Access old callback `Lua`.
... |
13 | | Ok(())
14 | | })?;
| |______________^ `scope` escapes the closure body here

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