Compare commits

...

156 Commits

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

* changelog entry
2024-08-06 20:04:17 +01:00
Alex Orlenko 10999babe0 Replace MultiValue::extend_from_values with from_lua_iter 2024-08-06 01:32:20 +01:00
Alex Orlenko f0a995a357 Make MultiValue::with_capacity public 2024-08-05 23:36:17 +01:00
Alex Orlenko aa47324ee9 Use pool for MultiValue container 2024-08-05 22:07:19 +01:00
Alex Orlenko ac6a391426 Remove Lua::into_static and Lua::from_static (undocumented). 2024-08-05 14:57:15 +01:00
Alex Orlenko 8e14b6e40b Fix tests 2024-08-04 22:22:02 +01:00
Alex Orlenko c117a4c1af Fix loading (fetching) stdlib modules when using require in Luau.
Fixes #435.
2024-08-04 18:55:17 +01:00
Alex Orlenko 4082b354fe clippy 2024-08-01 00:55:10 +01:00
Alex Orlenko 3641c98959 Prepare for Rust 2024 edition (see rust-lang/rust#123748)
Replace `IntoLua(Multi)` generic with positional arg (impl trait) where possible
This allow to shorten syntax from `a.get::<_, T>` to `a.get::<T>`
2024-07-31 23:42:43 +01:00
Alex Orlenko b7d170ab9b Refactor ThreadStatus:
- Add `ThreadStatus::Running`
- Replace `ThreadStatus::Unresumable` with `ThreadStatus::Finished`
Change `Error::CoroutineInactive` to `Error::CoroutineUnresumable`
2024-07-31 22:34:45 +01:00
123 changed files with 7444 additions and 5467 deletions
+4 -4
View File
@@ -6,18 +6,18 @@ jobs:
name: coverage
runs-on: ubuntu-latest
container:
image: xd009642/tarpaulin
image: xd009642/tarpaulin:develop-nightly
options: --security-opt seccomp=unconfined
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@main
- name: Generate coverage report
run: |
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
cargo +nightly tarpaulin --verbose --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
- name: Upload report to codecov.io
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: false
+63 -36
View File
@@ -7,18 +7,18 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -27,10 +27,11 @@ jobs:
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --features "${{ matrix.lua }},vendored"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-22.04' }}
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
@@ -44,23 +45,23 @@ jobs:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: aarch64-apple-darwin
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -71,18 +72,18 @@ jobs:
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,unstable"
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
shell: bash
build_armv7_cross_ubuntu:
name: Cross-compile to armv7-unknown-linux-gnueabihf
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -93,7 +94,7 @@ jobs:
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,unstable"
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
shell: bash
test:
@@ -102,18 +103,18 @@ jobs:
needs: build
strategy:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -122,14 +123,14 @@ jobs:
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --features "${{ matrix.lua }},vendored"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,unstable"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
run: |
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
shell: bash
test_with_sanitizer:
@@ -138,14 +139,14 @@ jobs:
needs: build
strategy:
matrix:
os: [ubuntu-22.04]
os: [ubuntu-latest]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -153,27 +154,54 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
env:
RUSTFLAGS: -Z sanitizer=address
test_with_memory_limit:
name: Test with memory limit
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-latest]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with forced memory limit
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
shell: bash
env:
RUSTFLAGS: --cfg=force_memory_limit
test_modules:
name: Test modules
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-22.04, macos-latest]
os: [ubuntu-latest, macos-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -197,7 +225,7 @@ jobs:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
- uses: actions/checkout@v4
- uses: actions/checkout@main
- name: Install Rust & Lua
run: |
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
@@ -208,13 +236,13 @@ jobs:
test_wasm32_emscripten:
name: Test on wasm32-unknown-emscripten
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luau]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -226,14 +254,13 @@ jobs:
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --tests --features "${{ matrix.lua }},vendored"
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,unstable"
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
rustfmt:
name: Rustfmt
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
@@ -241,12 +268,12 @@ jobs:
clippy:
name: Clippy
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
@@ -254,4 +281,4 @@ jobs:
- uses: giraffate/clippy-action@v1
with:
reporter: 'github-pr-review'
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
+54
View File
@@ -1,3 +1,57 @@
## v0.10.1 (Nov 9th, 2024)
- Minimal Luau updated to 0.650
- Added Luau native vector library support (this can change behavior if you use `vector` function!)
- Added Lua `String::display` method
- Improved pretty-printing for Lua tables (#478)
- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
- Added `AnyUserData::destroy` method
- New `userdata-wrappers` feature to `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` (similar to v0.9)
- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
- Added `Scope::add_destructor` to attach custom destructors
- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
- Added `From<Vec>` and `Into<Vec>` support to `MultiValue` and `Variadic` types
- Bug fixes and improvements (#477 #479)
## v0.10.0 (Oct 25th, 2024)
Changes since v0.10.0-rc.1
- Added `error-send` feature flag (disabled by default) to require `Send + Sync` for `Error`
- Some performance improvements
## v0.10.0-rc.1
- `Lua::scope` is back
- Support yielding from hooks for Lua 5.3+
- Support setting metatable for Lua builtin types (number/string/function/etc)
- Added `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` traits for using in `Function::wrap`
- Added `Error::chain` method to return iterator over nested errors
- Added `Lua::exec_raw` helper to execute low-level Lua C API code
- Added `Either<L, R>` enum to combine two types into a single one
- Added a new `Buffer` type for Luau
- Added `Value::is_error` and `Value::as_error` helpers
- Added `Value::Other` variant to represent unknown Lua types (eg LuaJIT CDATA)
- Added (optional) `anyhow` feature to implement `IntoLua` for `anyhow::Error`
- Added `IntoLua`/`FromLua` for `OsString`/`OsStr` and `PathBuf`/`Path`
## v0.10.0-beta.2
- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
- `Error::CoroutineInactive` renamed to `Error::CoroutineUnresumable`.
- `IntoLua`/`IntoLuaMulti` now uses `impl trait` syntax for args (shorten from `a.get::<_, T>` to `a.get::<T>`).
- Removed undocumented `Lua::into_static`/`from_static` methods.
- Futures now require `Send` bound if `send` feature is enabled.
- Dropped lifetime from `UserDataMethods` and `UserDataFields` traits.
- `Compiler::compile()` now returns `Result` (Luau).
- Removed `Clone` requirement from `UserDataFields::add_field()`.
- `TableExt` and `AnyUserDataExt` traits were combined into `ObjectLike` trait.
- Disabled `send` feature in module mode (since we don't have exclusive access to Lua).
- `Chunk::set_environment()` takes `Table` instead of `IntoLua` type.
- Reduced the compile time contribution of `next_key_seed` and `next_value_seed`.
- Reduced the compile time contribution of `serde_userdata`.
- Performance improvements.
## v0.10.0-beta.1
- Dropped `'lua` lifetime (subtypes now store a weak reference to Lua)
+11 -7
View File
@@ -1,8 +1,8 @@
[package]
name = "mlua"
version = "0.10.0-beta.1" # remember to update mlua_derive
version = "0.10.1" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
rust-version = "1.71"
rust-version = "1.79.0"
edition = "2021"
repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua"
@@ -16,7 +16,7 @@ with async/await features and support of writing native Lua modules in Rust.
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "unstable"]
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
@@ -38,14 +38,17 @@ luau-vector4 = ["luau", "ffi/luau-vector4"]
vendored = ["ffi/vendored"]
module = ["dep:mlua_derive", "ffi/module"]
async = ["dep:futures-util"]
send = []
send = ["parking_lot/send_guard", "error-send"]
error-send = []
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
macros = ["mlua_derive/macros"]
unstable = []
anyhow = ["dep:anyhow", "error-send"]
userdata-wrappers = []
[dependencies]
mlua_derive = { version = "=0.9.3", optional = true, path = "mlua_derive" }
mlua_derive = { version = "=0.10.0", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default-features = false }
either = "1.0"
num-traits = { version = "0.2.14" }
rustc-hash = "2.0"
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
@@ -53,8 +56,9 @@ serde = { version = "1.0", optional = true }
erased-serde = { version = "0.4", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", features = ["arc_lock"] }
anyhow = { version = "1.0", optional = true }
ffi = { package = "mlua-sys", version = "0.6.1", path = "mlua-sys" }
ffi = { package = "mlua-sys", version = "0.6.5", path = "mlua-sys" }
[target.'cfg(unix)'.dependencies]
libloading = { version = "0.8", optional = true }
+23 -21
View File
@@ -9,7 +9,7 @@
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
[MSRV]: https://img.shields.io/badge/rust-1.79+-brightgreen.svg?&logo=rust
[Guided Tour] | [Benchmarks] | [FAQ]
@@ -17,7 +17,9 @@
[Benchmarks]: https://github.com/khvzak/script-bench-rs
[FAQ]: FAQ.md
# The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`.
> **Note**
>
> See v0.10 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.10.md).
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
@@ -29,7 +31,7 @@ Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
[GitHub Actions]: https://github.com/khvzak/mlua/actions
[Roblox Luau]: https://luau-lang.org
[Roblox Luau]: https://luau.org
## Usage
@@ -38,23 +40,24 @@ WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for a
`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)
* `luau-jit`: activate [Luau] support with JIT backend.
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
* `lua54`: enable Lua [5.4] support
* `lua53`: enable Lua [5.3] support
* `lua52`: enable Lua [5.2] support
* `lua51`: enable Lua [5.1] support
* `luajit`: enable [LuaJIT] support
* `luajit52`: enable [LuaJIT] support with partial compatibility with Lua 5.2
* `luau`: enable [Luau] support (auto vendored mode)
* `luau-jit`: enable [Luau] support with JIT backend.
* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
* `module`: enable module mode (building loadable `cdylib` library for Lua)
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
* `error-send`: make `mlua:Error: Send + Sync`
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
* `macros`: enable procedural macros (such as `chunk!`)
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
* `unstable`: enable **unstable** features. The public API of these features may break between releases.
* `anyhow`: enable `anyhow::Error` conversion into Lua
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
[5.4]: https://www.lua.org/manual/5.4/manual.html
[5.3]: https://www.lua.org/manual/5.3/manual.html
@@ -68,7 +71,6 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
[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
@@ -92,7 +94,7 @@ cargo run --example async_http_client --features=lua54,async,macros
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
# async http server
cargo run --example async_http_server --features=lua54,async,macros
cargo run --example async_http_server --features=lua54,async,macros,send
curl -v http://localhost:3000
```
@@ -131,7 +133,7 @@ Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = { version = "0.9.9", features = ["lua54", "vendored"] }
mlua = { version = "0.10.1", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -166,7 +168,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.9.9", features = ["lua54", "module"] }
mlua = { version = "0.10.1", features = ["lua54", "module"] }
```
`lib.rs` :
@@ -196,7 +198,7 @@ $ lua5.4 -e 'require("my_module").hello("world")'
hello, world!
```
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config.toml` with the following content:
``` toml
[target.x86_64-apple-darwin]
rustflags = [
@@ -289,7 +291,7 @@ Please check the [Luau Sandboxing] page if you are interested in running untrust
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
[Luau Sandboxing]: https://luau-lang.org/sandbox
[Luau Sandboxing]: https://luau.org/sandbox
## License
+12 -18
View File
@@ -74,7 +74,7 @@ fn table_get_set(c: &mut Criterion) {
.enumerate()
{
table.raw_set(s, i).unwrap();
assert_eq!(table.raw_get::<_, usize>(s).unwrap(), i);
assert_eq!(table.raw_get::<usize>(s).unwrap(), i);
}
},
BatchSize::SmallInput,
@@ -153,7 +153,7 @@ fn function_call_sum(c: &mut Criterion) {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
@@ -172,7 +172,7 @@ fn function_call_lua_sum(c: &mut Criterion) {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
@@ -194,10 +194,7 @@ fn function_call_concat(c: &mut Criterion) {
i.fetch_add(1, Ordering::Relaxed)
},
|i| {
assert_eq!(
concat.call::<_, LuaString>(("num:", i)).unwrap(),
format!("num:{i}")
);
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
},
BatchSize::SmallInput,
);
@@ -220,10 +217,7 @@ fn function_call_lua_concat(c: &mut Criterion) {
i.fetch_add(1, Ordering::Relaxed)
},
|i| {
assert_eq!(
concat.call::<_, LuaString>(("num:", i)).unwrap(),
format!("num:{i}")
);
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
},
BatchSize::SmallInput,
);
@@ -246,7 +240,7 @@ fn function_async_call_sum(c: &mut Criterion) {
b.to_async(rt).iter_batched(
|| collect_gc_twice(&lua),
|_| async {
assert_eq!(sum.call_async::<_, i64>((10, 20, 30)).await.unwrap(), 0);
assert_eq!(sum.call_async::<i64>((10, 20, 30)).await.unwrap(), 0);
},
BatchSize::SmallInput,
);
@@ -303,7 +297,7 @@ fn userdata_create(c: &mut Criterion) {
fn userdata_call_index(c: &mut Criterion) {
struct UserData(#[allow(unused)] i64);
impl LuaUserData for UserData {
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
}
}
@@ -319,7 +313,7 @@ fn userdata_call_index(c: &mut Criterion) {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(index.call::<_, LuaString>(&ud).unwrap(), "test");
assert_eq!(index.call::<LuaString>(&ud).unwrap(), "test");
},
BatchSize::SmallInput,
);
@@ -329,7 +323,7 @@ fn userdata_call_index(c: &mut Criterion) {
fn userdata_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
}
}
@@ -349,7 +343,7 @@ fn userdata_call_method(c: &mut Criterion) {
i.fetch_add(1, Ordering::Relaxed)
},
|i| {
assert_eq!(method.call::<_, usize>((&ud, i)).unwrap(), 123 + i);
assert_eq!(method.call::<usize>((&ud, i)).unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
@@ -359,7 +353,7 @@ fn userdata_call_method(c: &mut Criterion) {
fn userdata_async_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("add", |_, this, i: i64| async move {
task::yield_now().await;
Ok(this.0 + i)
@@ -384,7 +378,7 @@ fn userdata_async_call_method(c: &mut Criterion) {
(method.clone(), ud.clone(), i.fetch_add(1, Ordering::Relaxed))
},
|(method, ud, i)| async move {
assert_eq!(method.call_async::<_, usize>((ud, i)).await.unwrap(), 123 + i);
assert_eq!(method.call_async::<usize>((ud, i)).await.unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
+2 -2
View File
@@ -37,7 +37,7 @@ fn encode_json(c: &mut Criterion) {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
encode.call::<_, LuaString>(&table).unwrap();
encode.call::<LuaString>(&table).unwrap();
},
BatchSize::SmallInput,
);
@@ -69,7 +69,7 @@ fn decode_json(c: &mut Criterion) {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
decode.call::<_, LuaTable>(json).unwrap();
decode.call::<LuaTable>(json).unwrap();
},
BatchSize::SmallInput,
);
+195
View File
@@ -0,0 +1,195 @@
## mlua v0.10 release notes
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
### New features
#### `'static` Lua types
In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
#### Truly `send` feature
In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
```rust
let lua = Lua::new();
lua.globals().set("i", 0)?;
let func = lua.load("i = i + ...").into_function()?;
std::thread::scope(|s| {
s.spawn(|| {
for i in 0..5 {
func.call::<()>(i).unwrap();
}
});
s.spawn(|| {
for i in 0..5 {
func.call::<()>(i).unwrap();
}
});
});
assert_eq!(lua.globals().get::<i32>("i")?, 20);
```
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
#### Register Rust functions with variable number of arguments
The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
```rust
let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
lua.globals().set("add", add).unwrap();
// Prints 50
lua.load(r#"print(add(5, 45))"#).exec().unwrap();
```
To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
#### Setting metatable for Lua builtin types
For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
```rust
let mt = lua.create_table()?;
mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
lua.set_type_metatable::<bool>(Some(mt));
lua.load("assert(tostring(true) == '2')").exec().unwrap();
```
### Improvements
#### New `ObjectLike` trait
The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
#### `Either<L, R>` enum
The `Either<L, R>` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
```rust
let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));
lua.globals().set("func", func).unwrap();
// Prints: received: 123
lua.load(r#"print(func(123))"#).exec().unwrap();
// Prints: bad argument #1: error converting Lua table to Either<i32, String>
lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
```
#### `Lua::exec_raw` helper to execute low-level Lua C API code
For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
```rust
let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
let sum: i64 = unsafe {
lua.exec_raw(&t, |state| {
// top of the stack: table `t`
let mut sum = 0;
// push nil as the first key
mlua::ffi::lua_pushnil(state);
while mlua::ffi::lua_next(state, -2) != 0 {
sum += mlua::ffi::lua_tointeger(state, -1);
// Remove the value, keep the key for the next iteration
mlua::ffi::lua_pop(state, 1);
}
mlua::ffi::lua_pop(state, 1);
mlua::ffi::lua_pushinteger(state, sum);
// top of the stack: sum
})
}?;
assert_eq!(sum, 15);
```
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
#### `anyhow` feature flag
The new `anyhow` feature flag adds `IntoLua` and `Into<mlua::Error>` implementation for the `anyhow::Error` type.
```rust
let f = lua.create_function(|_, ()| {
Err(anyhow!("error message"))?;
Ok(())
})?;
```
### Breaking changes
#### Scope changes
The following `Scope` methods were changed:
- Removed `Scope::create_any_userdata`
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
In mlua v0.9 this can cause read-after-free bug in some edge cases.
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
```rust
let data = "hello".to_string();
lua.scope(|scope| {
let ud = scope.create_any_userdata_ref(&data)?;
// We can only borrow scoped userdata using this method
ud.borrow_scoped::<String, ()>(|s| {
assert_eq!(s, "hello");
})?;
Ok(())
})?;
```
Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
#### String changes
Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
Lua instance can be destroyed while reference to the data is still alive:
```rust
let lua = Lua::new();
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
let s_ref: &str = s.to_str()?; // this is not safe!
drop(lua);
println!("{s_ref}"); // use after free!
```
To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
```rust
let lua = Lua::new();
let s: mlua::String = lua.create_string("hello, world")?;
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
drop(lua);
println!("{s_ref}"); // ok
```
The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
The same applies to `BorrowedBytes`.
Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
+2 -2
View File
@@ -10,9 +10,9 @@ use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
struct BodyReader(Incoming);
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
// Every call returns a next chunk
methods.add_async_method_mut("read", |lua, reader, ()| async move {
methods.add_async_method_mut("read", |lua, mut reader, ()| async move {
if let Some(bytes) = reader.0.frame().await {
if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
return Some(lua.create_string(&bytes)).transpose();
+5 -5
View File
@@ -17,7 +17,7 @@ use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table,
struct LuaRequest(SocketAddr, Request<Incoming>);
impl UserData for LuaRequest {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
@@ -47,13 +47,13 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
let handler = self.handler.clone();
let lua_req = LuaRequest(self.peer_addr, req);
Box::pin(async move {
match handler.call_async::<_, Table>(lua_req).await {
match handler.call_async::<Table>(lua_req).await {
Ok(lua_resp) => {
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
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")? {
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());
@@ -62,7 +62,7 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
// Set body
let body = lua_resp
.get::<_, Option<LuaString>>("body")?
.get::<Option<LuaString>>("body")?
.map(|b| Full::new(Bytes::copy_from_slice(&b.as_bytes())).boxed())
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
+7 -7
View File
@@ -4,27 +4,27 @@ use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use mlua::{chunk, Function, Lua, String as LuaString, UserData, UserDataMethods};
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
struct LuaTcpStream(TcpStream);
impl UserData for LuaTcpStream {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("peer_addr", |_, this, ()| Ok(this.0.peer_addr()?.to_string()));
methods.add_async_method_mut("read", |lua, this, size| async move {
methods.add_async_method_mut("read", |lua, mut this, size| async move {
let mut buf = vec![0; size];
let n = this.0.read(&mut buf).await?;
buf.truncate(n);
lua.create_string(&buf)
});
methods.add_async_method_mut("write", |_, this, data: LuaString| async move {
let n = this.0.write(&data.as_bytes()).await?;
methods.add_async_method_mut("write", |_, mut this, data: BString| async move {
let n = this.0.write(&data).await?;
Ok(n)
});
methods.add_async_method_mut("close", |_, this, ()| async move {
methods.add_async_method_mut("close", |_, mut this, ()| async move {
this.0.shutdown().await?;
Ok(())
});
@@ -47,7 +47,7 @@ async fn run_server(handler: Function) -> io::Result<()> {
let handler = handler.clone();
tokio::task::spawn(async move {
let stream = LuaTcpStream(stream);
if let Err(err) = handler.call_async::<_, ()>(stream).await {
if let Err(err) = handler.call_async::<()>(stream).await {
eprintln!("{}", err);
}
});
+7 -7
View File
@@ -17,8 +17,8 @@ fn main() -> Result<()> {
globals.set("string_var", "hello")?;
globals.set("int_var", 42)?;
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
assert_eq!(globals.get::<String>("string_var")?, "hello");
assert_eq!(globals.get::<i64>("int_var")?, 42);
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
// that allows you to change settings before running Lua code. Here, we are using it to set
@@ -32,7 +32,7 @@ fn main() -> Result<()> {
)
.set_name("example code")
.exec()?;
assert_eq!(globals.get::<_, String>("global")?, "foobar");
assert_eq!(globals.get::<String>("global")?, "foobar");
assert_eq!(lua.load("1 + 1").eval::<i32>()?, 2);
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
@@ -85,16 +85,16 @@ fn main() -> Result<()> {
// You can load Lua functions
let print: Function = globals.get("print")?;
print.call::<_, ()>("hello from rust")?;
print.call::<()>("hello from rust")?;
// This API generally handles variadic using tuples. This is one way to call a function with
// multiple parameters:
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
print.call::<()>(("hello", "again", "from", "rust"))?;
// But, you can also pass variadic arguments with the `Variadic` type.
print.call::<_, ()>(Variadic::from_iter(
print.call::<()>(Variadic::from_iter(
["hello", "yet", "again", "from", "rust"].iter().cloned(),
))?;
@@ -162,7 +162,7 @@ fn main() -> Result<()> {
}
impl UserData for Vec2 {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("magnitude", |_, vec, ()| {
let mag_squared = vec.0 * vec.0 + vec.1 * vec.1;
Ok(mag_squared.sqrt())
+2 -2
View File
@@ -7,7 +7,7 @@ struct Rectangle {
}
impl UserData for Rectangle {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("length", |_, this| Ok(this.length));
fields.add_field_method_set("length", |_, this, val| {
this.length = val;
@@ -20,7 +20,7 @@ impl UserData for Rectangle {
});
}
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
methods.add_method("diagonal", |_, this, ()| {
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua-sys"
version = "0.6.2"
version = "0.6.5"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
rust-version = "1.71"
edition = "2021"
@@ -40,7 +40,7 @@ cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 547.0.0, < 547.1.0", optional = true }
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
luau0-src = { version = "0.10.0", optional = true }
luau0-src = { version = "0.11.1", optional = true }
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
+24 -5
View File
@@ -176,7 +176,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
#[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"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
@@ -314,7 +314,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
#[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"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
@@ -403,6 +403,25 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
}
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setfenv(L, -2);
}
status
}
#[inline(always)]
pub unsafe fn luaL_loadbufferx(
L: *mut lua_State,
@@ -425,7 +444,7 @@ pub unsafe fn luaL_loadbufferx(
#[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"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
@@ -507,14 +526,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
#[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_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
+19
View File
@@ -247,3 +247,22 @@ pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lu
}
lua_replace(L, -2);
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setupvalue(L, -2, 1);
}
status
}
+21 -1
View File
@@ -1,7 +1,8 @@
//! MLua compatibility layer for Lua 5.3
use std::os::raw::c_int;
use std::os::raw::{c_char, c_int};
use super::lauxlib::*;
use super::lua::*;
#[inline(always)]
@@ -12,3 +13,22 @@ pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, n
}
ret
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setupvalue(L, -2, 1);
}
status
}
+19
View File
@@ -169,6 +169,25 @@ pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n:
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua::lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == lua::LUA_OK && env != 0 {
lua::lua_pushvalue(L, env);
lua::lua_setupvalue(L, -2, 1);
}
status
}
//
// TODO: Generic Buffer Manipulation
//
+21 -9
View File
@@ -108,7 +108,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
#[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"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
@@ -217,7 +217,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
#[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"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
@@ -320,12 +320,13 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
}
}
pub unsafe fn luaL_loadbufferx(
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
mut size: usize,
name: *const c_char,
mode: *const c_char,
env: c_int,
) -> c_int {
extern "C" {
fn free(p: *mut c_void);
@@ -345,17 +346,28 @@ pub unsafe fn luaL_loadbufferx(
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;
let ok = luau_load(L, name, data, size, env) == 0;
free(data as *mut c_void);
if !ok {
return LUA_ERRSYNTAX;
}
} else if luau_load(L, name, data, size, 0) != 0 {
} else if luau_load(L, name, data, size, env) != 0 {
return LUA_ERRSYNTAX;
}
LUA_OK
}
#[inline(always)]
pub unsafe fn luaL_loadbufferx(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int {
luaL_loadbufferenv(L, data, size, name, mode, 0)
}
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
@@ -363,13 +375,13 @@ pub unsafe fn luaL_loadbuffer(
size: usize,
name: *const c_char,
) -> c_int {
luaL_loadbufferx(L, data, size, name, ptr::null())
luaL_loadbufferenv(L, data, size, name, ptr::null(), 0)
}
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
@@ -451,14 +463,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
#[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_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
+5
View File
@@ -185,6 +185,7 @@ extern "C-unwind" {
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
pub fn lua_newuserdatataggedwithmetatable(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
@@ -526,6 +527,9 @@ pub struct lua_Callbacks {
pub debuginterrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when protected call results in an error
pub debugprotectederror: Option<unsafe extern "C-unwind" fn(L: *mut lua_State)>,
/// gets called when memory is allocated
pub onallocate: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, osize: usize, nsize: usize)>,
}
extern "C" {
@@ -535,4 +539,5 @@ extern "C" {
// Functions from customization lib
extern "C" {
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
}
+2
View File
@@ -13,6 +13,7 @@ pub const LUA_BUFFERLIBNAME: &str = "buffer";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_VECLIBNAME: &str = "vector";
extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
@@ -25,6 +26,7 @@ extern "C-unwind" {
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.9.3"
version = "0.10.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
description = "Procedural macros for the mlua crate."
@@ -19,6 +19,6 @@ quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error = { version = "1.0", optional = true }
syn = { version = "2.0", features = ["full"] }
itertools = { version = "0.12", optional = true }
itertools = { version = "0.13", optional = true }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.0", optional = true }
+1 -1
View File
@@ -20,7 +20,7 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => Err(::mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: #ident_str,
to: #ident_str.to_string(),
message: None,
}),
}
+4 -3
View File
@@ -64,9 +64,10 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
#[no_mangle]
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
let lua = mlua::Lua::init_from_ptr(state);
#skip_memory_check
lua.entrypoint1(state, #func_name)
mlua::Lua::entrypoint1(state, move |lua| {
#skip_memory_check
#func_name(lua)
})
}
};
+86
View File
@@ -0,0 +1,86 @@
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, Serializer};
use crate::types::ValueRef;
/// A Luau buffer type.
///
/// See the buffer [documentation] for more information.
///
/// [documentation]: https://luau.org/library#buffer-library
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq)]
pub struct Buffer(pub(crate) ValueRef);
#[cfg_attr(not(feature = "luau"), allow(unused))]
impl Buffer {
/// Copies the buffer data into a new `Vec<u8>`.
pub fn to_vec(&self) -> Vec<u8> {
unsafe { self.as_slice().to_vec() }
}
/// Returns the length of the buffer.
pub fn len(&self) -> usize {
unsafe { self.as_slice().len() }
}
/// Returns `true` if the buffer is empty.
#[doc(hidden)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Reads given number of bytes from the buffer at the given offset.
///
/// Offset is 0-based.
#[track_caller]
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
let data = unsafe { self.as_slice() };
let mut bytes = [0u8; N];
bytes.copy_from_slice(&data[offset..offset + N]);
bytes
}
/// Writes given bytes to the buffer at the given offset.
///
/// Offset is 0-based.
#[track_caller]
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
let data = unsafe {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts_mut(buf, size)
};
data[offset..offset + bytes.len()].copy_from_slice(bytes);
}
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts(buf, size)
}
#[cfg(feature = "luau")]
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
let lua = self.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
(buf as *mut u8, size)
}
#[cfg(not(feature = "luau"))]
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
unreachable!()
}
}
#[cfg(feature = "serialize")]
impl Serialize for Buffer {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
serializer.serialize_bytes(unsafe { self.as_slice() })
}
}
#[cfg(feature = "luau")]
impl crate::types::LuaType for Buffer {
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
}
+41 -34
View File
@@ -5,16 +5,15 @@ use std::io::Result as IoResult;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use crate::error::{Error, ErrorContext, Result};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, WeakLua};
use crate::table::Table;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::traits::{FromLuaMulti, IntoLuaMulti};
/// 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<'a> {
/// Returns optional chunk name
fn name(&self) -> Option<StdString> {
@@ -95,8 +94,6 @@ impl AsChunk<'static> for PathBuf {
}
/// 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<'a> {
pub(crate) lua: WeakLua,
@@ -134,7 +131,7 @@ pub struct Compiler {
#[cfg(any(feature = "luau", doc))]
impl Default for Compiler {
fn default() -> Self {
Self::new()
const { Self::new() }
}
}
@@ -240,7 +237,9 @@ impl Compiler {
}
/// Compiles the `source` into bytecode.
pub fn compile(&self, source: impl AsRef<[u8]>) -> Vec<u8> {
///
/// Returns [`Error::SyntaxError`] if the source code is invalid.
pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>> {
use std::os::raw::c_int;
use std::ptr;
@@ -274,7 +273,7 @@ impl Compiler {
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
unsafe {
let bytecode = unsafe {
let mut options = ffi::lua_CompileOptions::default();
options.optimizationLevel = self.optimization_level as c_int;
options.debugLevel = self.debug_level as c_int;
@@ -286,11 +285,23 @@ impl Compiler {
options.mutableGlobals = mutable_globals_ptr;
options.userdataTypes = userdata_types_ptr;
ffi::luau_compile(source.as_ref(), options)
};
if bytecode.first() == Some(&0) {
// The rest of the bytecode is the error message starting with `:`
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
let message = String::from_utf8_lossy(&bytecode[2..]).to_string();
return Err(Error::SyntaxError {
incomplete_input: message.ends_with("<eof>"),
message,
});
}
Ok(bytecode)
}
}
impl<'a> Chunk<'a> {
impl Chunk<'_> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
@@ -308,13 +319,8 @@ impl<'a> Chunk<'a> {
/// 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: IntoLua>(mut self, env: V) -> Self {
let lua = self.lua.lock();
let lua = lua.lua();
self.env = env
.into_lua(lua)
.and_then(|val| lua.unpack(val))
.context("bad environment value");
pub fn set_environment(mut self, env: Table) -> Self {
self.env = Ok(Some(env));
self
}
@@ -343,8 +349,7 @@ impl<'a> Chunk<'a> {
///
/// This is equivalent to calling the chunk function with no arguments and no return values.
pub fn exec(self) -> Result<()> {
self.call::<_, ()>(())?;
Ok(())
self.call(())
}
/// Asynchronously execute this chunk of code.
@@ -353,7 +358,7 @@ impl<'a> Chunk<'a> {
///
/// Requires `feature = "async"`
///
/// [`exec`]: #method.exec
/// [`exec`]: Chunk::exec
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn exec_async(self) -> Result<()> {
@@ -385,7 +390,7 @@ impl<'a> Chunk<'a> {
///
/// Requires `feature = "async"`
///
/// [`eval`]: #method.eval
/// [`eval`]: Chunk::eval
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn eval_async<R>(self) -> Result<R>
@@ -404,7 +409,7 @@ impl<'a> Chunk<'a> {
/// 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: IntoLuaMulti, R: FromLuaMulti>(self, args: A) -> Result<R> {
pub fn call<R: FromLuaMulti>(self, args: impl IntoLuaMulti) -> Result<R> {
self.into_function()?.call(args)
}
@@ -414,18 +419,17 @@ impl<'a> Chunk<'a> {
///
/// Requires `feature = "async"`
///
/// [`call`]: #method.call
/// [`call`]: Chunk::call
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn call_async<A, R>(self, args: A) -> Result<R>
pub async fn call_async<R>(self, args: impl IntoLuaMulti) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.into_function()?.call_async(args).await
}
/// Load this chunk into a regular `Function`.
/// Load this chunk into a regular [`Function`].
///
/// This simply compiles the chunk without actually executing it.
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
@@ -439,18 +443,17 @@ impl<'a> Chunk<'a> {
let name = Self::convert_name(self.name)?;
self.lua
.lock()
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
.load_chunk(Some(&name), self.env?.as_ref(), self.mode, self.source?.as_ref())
}
/// Compiles the chunk and changes mode to binary.
///
/// It does nothing if the chunk is already binary.
/// It does nothing if the chunk is already binary or invalid.
fn compile(&mut self) {
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
#[cfg(feature = "luau")]
{
let data = self.compiler.get_or_insert_with(Default::default).compile(source);
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
@@ -475,7 +478,7 @@ impl<'a> Chunk<'a> {
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
let lua = self.lua.lock();
if let Some(cache) = lua.app_data_ref::<ChunksCache>() {
if let Some(cache) = lua.app_data_ref_unguarded::<ChunksCache>() {
if let Some(data) = cache.0.get(source.as_ref()) {
self.source = Ok(Cow::Owned(data.clone()));
self.mode = Some(ChunkMode::Binary);
@@ -492,7 +495,7 @@ impl<'a> Chunk<'a> {
if let Ok(ref binary_source) = self.source {
if self.detect_mode() == ChunkMode::Binary {
let lua = self.lua.lock();
if let Some(mut cache) = lua.app_data_mut::<ChunksCache>() {
if let Some(mut cache) = lua.app_data_mut_unguarded::<ChunksCache>() {
cache.0.insert(text_source, binary_source.as_ref().to_vec());
} else {
let mut cache = ChunksCache(HashMap::new());
@@ -517,12 +520,16 @@ impl<'a> Chunk<'a> {
.compiler
.as_ref()
.map(|c| c.compile(&source))
.transpose()?
.unwrap_or(source);
let name = Self::convert_name(self.name.clone())?;
self.lua
.lock()
.load_chunk(Some(&name), self.env.clone()?, None, &source)
let env = match &self.env {
Ok(Some(env)) => Some(env),
Ok(None) => None,
Err(err) => return Err(err.clone()),
};
self.lua.lock().load_chunk(Some(&name), env, None, &source)
}
fn detect_mode(&self) -> ChunkMode {
+250 -55
View File
@@ -1,12 +1,13 @@
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::ffi::{CStr, CString};
use std::ffi::{CStr, CString, OsStr, OsString};
use std::hash::{BuildHasher, Hash};
use std::os::raw::c_int;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use std::{slice, str};
use bstr::{BStr, BString};
use bstr::{BStr, BString, ByteSlice, ByteVec};
use num_traits::cast;
use crate::error::{Error, Result};
@@ -15,9 +16,10 @@ use crate::state::{Lua, RawLua};
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{LightUserData, MaybeSend, RegistryKey};
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
use crate::types::{Either, LightUserData, MaybeSend, RegistryKey};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, IntoLua, Nil, Value};
use crate::value::{Nil, Value};
impl IntoLua for Value {
#[inline]
@@ -72,10 +74,21 @@ impl FromLua for String {
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "string",
to: "string".to_string(),
message: Some("expected string or number".to_string()),
})
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let state = lua.state();
let type_id = ffi::lua_type(state, idx);
if type_id == ffi::LUA_TSTRING {
ffi::lua_xpush(state, lua.ref_thread(), idx);
return Ok(String(lua.pop_ref_thread()));
}
// Fallback to default
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
}
}
impl IntoLua for Table {
@@ -105,7 +118,7 @@ impl FromLua for Table {
Value::Table(table) => Ok(table),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "table",
to: "table".to_string(),
message: None,
}),
}
@@ -139,7 +152,7 @@ impl FromLua for Function {
Value::Function(table) => Ok(table),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "function",
to: "function".to_string(),
message: None,
}),
}
@@ -173,7 +186,7 @@ impl FromLua for Thread {
Value::Thread(t) => Ok(t),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "thread",
to: "thread".to_string(),
message: None,
}),
}
@@ -207,7 +220,7 @@ impl FromLua for AnyUserData {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
to: "userdata".to_string(),
message: None,
}),
}
@@ -230,18 +243,22 @@ impl IntoLua for Error {
impl FromLua for Error {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Error> {
fn from_lua(value: Value, _: &Lua) -> Result<Error> {
match value {
Value::Error(err) => Ok(*err),
val => Ok(Error::runtime(
lua.coerce_string(val)?
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
.unwrap_or_else(|| "<unprintable error>".to_owned()),
)),
val => Ok(Error::runtime(val.to_string()?)),
}
}
}
#[cfg(feature = "anyhow")]
impl IntoLua for anyhow::Error {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Error(Box::new(Error::from(self))))
}
}
impl IntoLua for RegistryKey {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
@@ -325,7 +342,7 @@ impl FromLua for LightUserData {
Value::LightUserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "light userdata",
to: "lightuserdata".to_string(),
message: None,
}),
}
@@ -333,7 +350,7 @@ impl FromLua for LightUserData {
}
#[cfg(feature = "luau")]
impl IntoLua for crate::types::Vector {
impl IntoLua for crate::Vector {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Vector(self))
@@ -341,14 +358,51 @@ impl IntoLua for crate::types::Vector {
}
#[cfg(feature = "luau")]
impl FromLua for crate::types::Vector {
impl FromLua for crate::Vector {
#[inline]
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
match value {
Value::Vector(v) => Ok(v),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "vector",
to: "vector".to_string(),
message: None,
}),
}
}
}
#[cfg(feature = "luau")]
impl IntoLua for crate::Buffer {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Buffer(self))
}
}
#[cfg(feature = "luau")]
impl IntoLua for &crate::Buffer {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Buffer(self.clone()))
}
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_ref(&self.0);
Ok(())
}
}
#[cfg(feature = "luau")]
impl FromLua for crate::Buffer {
#[inline]
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
match value {
Value::Buffer(buf) => Ok(buf),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "buffer".to_string(),
message: None,
}),
}
@@ -375,7 +429,7 @@ impl FromLua for StdString {
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "String",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?
.to_str()?
@@ -385,7 +439,8 @@ impl FromLua for StdString {
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let state = lua.state();
if ffi::lua_type(state, idx) == ffi::LUA_TSTRING {
let type_id = ffi::lua_type(state, idx);
if type_id == ffi::LUA_TSTRING {
let mut size = 0;
let data = ffi::lua_tolstring(state, idx, &mut size);
let bytes = slice::from_raw_parts(data as *const u8, size);
@@ -393,12 +448,12 @@ impl FromLua for StdString {
.map(|s| s.to_owned())
.map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "String",
to: Self::type_name(),
message: Some(e.to_string()),
});
}
// Fallback to default
Self::from_lua(lua.stack_value(idx), lua.lua())
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
}
}
@@ -436,7 +491,7 @@ impl FromLua for Box<str> {
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "Box<str>",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?
.to_str()?
@@ -460,7 +515,7 @@ impl FromLua for CString {
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "CString",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?;
@@ -468,7 +523,7 @@ impl FromLua for CString {
Ok(s) => Ok(s.into()),
Err(_) => Err(Error::FromLuaConversionError {
from: ty,
to: "CString",
to: Self::type_name(),
message: Some("invalid C-style string".to_string()),
}),
}
@@ -502,18 +557,12 @@ impl FromLua for BString {
match value {
Value::String(s) => Ok((*s.as_bytes()).into()),
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let lua = ud.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
Ok(slice::from_raw_parts(buf as *const u8, size).into())
},
Value::Buffer(buf) => unsafe { Ok(buf.as_slice().into()) },
_ => Ok((*lua
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "BString",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?
.as_bytes())
@@ -536,9 +585,9 @@ impl FromLua for BString {
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
Ok(slice::from_raw_parts(buf as *const u8, size).into())
}
_ => {
type_id => {
// Fallback to default
Self::from_lua(lua.stack_value(idx), lua.lua())
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
}
}
}
@@ -551,6 +600,61 @@ impl IntoLua for &BStr {
}
}
impl IntoLua for OsString {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
self.as_os_str().into_lua(lua)
}
}
impl FromLua for OsString {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let ty = value.type_name();
let bs = BString::from_lua(value, lua)?;
Vec::from(bs)
.into_os_string()
.map_err(|err| Error::FromLuaConversionError {
from: ty,
to: "OsString".into(),
message: Some(err.to_string()),
})
}
}
impl IntoLua for &OsStr {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
from: "OsStr".into(),
to: "string",
message: Some("invalid utf-8 encoding".into()),
})?;
Ok(Value::String(lua.create_string(s)?))
}
}
impl IntoLua for PathBuf {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
self.as_os_str().into_lua(lua)
}
}
impl FromLua for PathBuf {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
OsString::from_lua(value, lua).map(PathBuf::from)
}
}
impl IntoLua for &Path {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
self.as_os_str().into_lua(lua)
}
}
#[inline]
unsafe fn push_bytes_into_stack<T>(this: T, lua: &RawLua) -> Result<()>
where
@@ -576,7 +680,7 @@ macro_rules! lua_convert_int {
.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),
from: stringify!($x).to_string(),
to: "number",
message: Some("out of range".to_owned()),
})
@@ -607,7 +711,7 @@ macro_rules! lua_convert_int {
lua.coerce_number(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some(
"expected number or string coercible to number".to_string(),
),
@@ -618,10 +722,28 @@ macro_rules! lua_convert_int {
})
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
})
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let state = lua.state();
let type_id = ffi::lua_type(state, idx);
if type_id == ffi::LUA_TNUMBER {
let mut ok = 0;
let i = ffi::lua_tointegerx(state, idx, &mut ok);
if ok != 0 {
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
from: "integer",
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
});
}
}
// Fallback to default
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
}
}
};
}
@@ -646,7 +768,7 @@ macro_rules! lua_convert_float {
fn into_lua(self, _: &Lua) -> Result<Value> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
from: stringify!($x).to_string(),
to: "number",
message: Some("out of range".to_string()),
})
@@ -661,17 +783,35 @@ macro_rules! lua_convert_float {
lua.coerce_number(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("expected number or string coercible to number".to_string()),
})
.and_then(|n| {
cast(n).ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("number out of range".to_string()),
})
})
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let state = lua.state();
let type_id = ffi::lua_type(state, idx);
if type_id == ffi::LUA_TNUMBER {
let mut ok = 0;
let i = ffi::lua_tonumberx(state, idx, &mut ok);
if ok != 0 {
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
from: "number",
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
});
}
}
// Fallback to default
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
}
}
};
}
@@ -708,7 +848,7 @@ where
match value {
#[cfg(feature = "luau")]
#[rustfmt::skip]
Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
Value::Vector(v) if N == crate::Vector::SIZE => unsafe {
use std::{mem, ptr};
let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
@@ -723,13 +863,13 @@ where
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())),
to: Self::type_name(),
message: Some(format!("expected table of length {N}, got {}", vec.len())),
})
}
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Array",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -764,7 +904,7 @@ impl<T: FromLua> FromLua for Vec<T> {
Value::Table(table) => table.sequence_values().collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Vec",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -786,7 +926,7 @@ impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for H
} else {
Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "HashMap",
to: Self::type_name(),
message: Some("expected table".to_string()),
})
}
@@ -808,7 +948,7 @@ impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
} else {
Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "BTreeMap",
to: Self::type_name(),
message: Some("expected table".to_string()),
})
}
@@ -832,7 +972,7 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "HashSet",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -856,7 +996,7 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "BTreeSet",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -893,10 +1033,65 @@ impl<T: FromLua> FromLua for Option<T> {
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
if ffi::lua_isnil(lua.state(), idx) != 0 {
Ok(None)
} else {
Ok(Some(T::from_stack(idx, lua)?))
match ffi::lua_type(lua.state(), idx) {
ffi::LUA_TNIL => Ok(None),
_ => Ok(Some(T::from_stack(idx, lua)?)),
}
}
}
impl<L: IntoLua, R: IntoLua> IntoLua for Either<L, R> {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
match self {
Either::Left(l) => l.into_lua(lua),
Either::Right(r) => r.into_lua(lua),
}
}
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
match self {
Either::Left(l) => l.push_into_stack(lua),
Either::Right(r) => r.push_into_stack(lua),
}
}
}
impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let value_type_name = value.type_name();
// Try the left type first
match L::from_lua(value.clone(), lua) {
Ok(l) => Ok(Either::Left(l)),
// Try the right type
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
Ok(r) => Ok(r),
Err(_) => Err(Error::FromLuaConversionError {
from: value_type_name,
to: Self::type_name(),
message: None,
}),
},
}
}
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
match L::from_stack(idx, lua) {
Ok(l) => Ok(Either::Left(l)),
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
Ok(r) => Ok(r),
Err(_) => {
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
Err(Error::FromLuaConversionError {
from: value_type_name.to_str().unwrap(),
to: Self::type_name(),
message: None,
})
}
},
}
}
}
+136 -55
View File
@@ -9,6 +9,12 @@ use std::sync::Arc;
use crate::private::Sealed;
#[cfg(feature = "error-send")]
type DynStdError = dyn StdError + Send + Sync;
#[cfg(not(feature = "error-send"))]
type DynStdError = dyn StdError;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
#[non_exhaustive]
@@ -42,11 +48,11 @@ pub enum Error {
GarbageCollectorError(StdString),
/// Potentially unsafe action in safe mode.
SafetyError(StdString),
/// Setting memory limit is not available.
/// Memory control is not available.
///
/// This error can only happen when Lua state was not created by us and does not have the
/// custom allocator attached.
MemoryLimitNotAvailable,
MemoryControlNotAvailable,
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
///
/// This is an error because a mutable callback can only be borrowed mutably once.
@@ -61,10 +67,12 @@ pub enum Error {
///
/// Due to the way `mlua` works, it should not be directly possible to run out of stack space
/// during normal use. The only way that this error can be triggered is if a `Function` is
/// called with a huge number of arguments, or a rust callback returns a huge number of return
/// called with a huge number of arguments, or a Rust callback returns a huge number of return
/// values.
StackError,
/// Too many arguments to `Function::bind`.
/// Too many arguments to [`Function::bind`].
///
/// [`Function::bind`]: crate::Function::bind
BindError,
/// Bad argument received from Lua (usually when calling a function).
///
@@ -83,7 +91,7 @@ pub enum Error {
/// A Rust value could not be converted to a Lua value.
ToLuaConversionError {
/// Name of the Rust type that could not be converted.
from: &'static str,
from: String,
/// Name of the Lua type that could not be created.
to: &'static str,
/// A message indicating why the conversion failed in more detail.
@@ -94,21 +102,21 @@ pub enum Error {
/// Name of the Lua type that could not be converted.
from: &'static str,
/// Name of the Rust type that could not be created.
to: &'static str,
to: String,
/// A string containing more detailed error information.
message: Option<StdString>,
},
/// [`Thread::resume`] was called on an inactive coroutine.
/// [`Thread::resume`] was called on an unresumable coroutine.
///
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
/// the coroutine. Already running coroutines are also marked as inactive (unresumable).
/// A coroutine is unresumable if its main function has returned or if an error has occurred
/// inside the coroutine. Already running coroutines are also marked as unresumable.
///
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
/// error.
///
/// [`Thread::resume`]: crate::Thread::resume
/// [`Thread::status`]: crate::Thread::status
CoroutineInactive,
CoroutineUnresumable,
/// An [`AnyUserData`] is not the expected type in a borrow.
///
/// This error can only happen when manually using [`AnyUserData`], or when implementing
@@ -189,7 +197,7 @@ pub enum Error {
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered.
ExternalError(Arc<dyn StdError + Send + Sync>),
ExternalError(Arc<DynStdError>),
/// An error with additional context.
WithContext {
/// A string containing additional context.
@@ -205,21 +213,21 @@ pub type Result<T> = StdResult<T, Error>;
#[cfg(not(tarpaulin_include))]
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {msg}"),
Error::MemoryError(ref msg) => {
match self {
Error::SyntaxError { message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(msg) => write!(fmt, "runtime error: {msg}"),
Error::MemoryError(msg) => {
write!(fmt, "memory error: {msg}")
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
Error::GarbageCollectorError(ref msg) => {
Error::GarbageCollectorError(msg) => {
write!(fmt, "garbage collector error: {msg}")
}
Error::SafetyError(ref msg) => {
Error::SafetyError(msg) => {
write!(fmt, "safety error: {msg}")
},
Error::MemoryLimitNotAvailable => {
write!(fmt, "setting memory limit is not available")
Error::MemoryControlNotAvailable => {
write!(fmt, "memory control is not available")
}
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
Error::CallbackDestructed => write!(
@@ -234,7 +242,7 @@ impl fmt::Display for Error {
fmt,
"too many arguments to Function::bind"
),
Error::BadArgument { ref to, pos, ref name, ref cause } => {
Error::BadArgument { to, pos, name, cause } => {
if let Some(name) = name {
write!(fmt, "bad argument `{name}`")?;
} else {
@@ -245,40 +253,40 @@ impl fmt::Display for Error {
}
write!(fmt, ": {cause}")
},
Error::ToLuaConversionError { from, to, ref message } => {
Error::ToLuaConversionError { from, to, message } => {
write!(fmt, "error converting {from} to Lua {to}")?;
match *message {
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::FromLuaConversionError { from, to, ref message } => {
Error::FromLuaConversionError { from, to, message } => {
write!(fmt, "error converting Lua {from} to {to}")?;
match *message {
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { method, type_name, message } => {
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
match *message {
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref cause, ref traceback } => {
Error::CallbackError { cause, traceback } => {
// 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 {
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
cause = cause2;
full_traceback = Some(traceback2);
}
@@ -302,15 +310,15 @@ impl fmt::Display for Error {
write!(fmt, "previously resumed panic returned again")
}
#[cfg(feature = "serialize")]
Error::SerializeError(ref err) => {
Error::SerializeError(err) => {
write!(fmt, "serialize error: {err}")
},
#[cfg(feature = "serialize")]
Error::DeserializeError(ref err) => {
Error::DeserializeError(err) => {
write!(fmt, "deserialize error: {err}")
},
Error::ExternalError(ref err) => write!(fmt, "{err}"),
Error::WithContext { ref context, ref cause } => {
Error::ExternalError(err) => err.fmt(fmt),
Error::WithContext { context, cause } => {
writeln!(fmt, "{context}")?;
write!(fmt, "{cause}")
}
@@ -320,18 +328,15 @@ impl fmt::Display for Error {
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
match self {
// An error type with a source error should either return that error via source or
// include that source's error message in its own Display output, but never both.
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
// returns nothing.
Error::CallbackError { .. } => None,
Error::ExternalError(ref err) => err.source(),
Error::WithContext { ref cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.source(),
_ => None,
},
Error::ExternalError(err) => err.source(),
Error::WithContext { cause, .. } => Self::source(cause),
_ => None,
}
}
@@ -346,7 +351,7 @@ impl Error {
/// Wraps an external error object.
#[inline]
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
Error::ExternalError(err.into().into())
}
@@ -357,10 +362,25 @@ impl Error {
{
match self {
Error::ExternalError(err) => err.downcast_ref(),
Error::WithContext { cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.downcast_ref(),
_ => None,
},
Error::WithContext { cause, .. } => Self::downcast_ref(cause),
_ => None,
}
}
/// An iterator over the chain of nested errors wrapped by this Error.
pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
Chain {
root: self,
current: None,
}
}
/// Returns the parent of this error.
#[doc(hidden)]
pub fn parent(&self) -> Option<&Error> {
match self {
Error::CallbackError { cause, .. } => Some(cause.as_ref()),
Error::WithContext { cause, .. } => Some(cause.as_ref()),
_ => None,
}
}
@@ -374,15 +394,15 @@ impl Error {
}
}
pub(crate) fn from_lua_conversion<'a>(
pub(crate) fn from_lua_conversion(
from: &'static str,
to: &'static str,
message: impl Into<Option<&'a str>>,
to: impl ToString,
message: impl Into<Option<String>>,
) -> Self {
Error::FromLuaConversionError {
from,
to,
message: message.into().map(|s| s.into()),
to: to.to_string(),
message: message.into(),
}
}
}
@@ -392,7 +412,7 @@ pub trait ExternalError {
fn into_lua_err(self) -> Error;
}
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
impl<E: Into<Box<DynStdError>>> ExternalError for E {
fn into_lua_err(self) -> Error {
Error::external(self)
}
@@ -446,7 +466,7 @@ impl ErrorContext for Error {
}
}
impl<T> ErrorContext for StdResult<T, Error> {
impl<T> ErrorContext for Result<T> {
fn context<C: fmt::Display>(self, context: C) -> Self {
self.map_err(|err| err.context(context))
}
@@ -487,3 +507,64 @@ impl serde::de::Error for Error {
Self::DeserializeError(msg.to_string())
}
}
#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for Error {
fn from(err: anyhow::Error) -> Self {
match err.downcast::<Self>() {
Ok(err) => err,
Err(err) => Error::external(err),
}
}
}
struct Chain<'a> {
root: &'a Error,
current: Option<&'a (dyn StdError + 'static)>,
}
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn StdError + 'static);
fn next(&mut self) -> Option<Self::Item> {
loop {
let error: Option<&dyn StdError> = match self.current {
None => {
self.current = Some(self.root);
self.current
}
Some(current) => match current.downcast_ref::<Error>()? {
Error::BadArgument { cause, .. }
| Error::CallbackError { cause, .. }
| Error::WithContext { cause, .. } => {
self.current = Some(&**cause);
self.current
}
Error::ExternalError(err) => {
self.current = Some(&**err);
self.current
}
_ => None,
},
};
// Skip `ExternalError` as it only wraps the underlying error
// without meaningful context
if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
continue;
}
return self.current;
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "error-send"))]
static_assertions::assert_not_impl_any!(Error: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Error: Send, Sync);
}
+93 -39
View File
@@ -5,20 +5,22 @@ use std::{mem, ptr, slice};
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::table::Table;
use crate::types::{Callback, MaybeSend, ValueRef};
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
use crate::util::{
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::Value;
#[cfg(feature = "async")]
use {
crate::traits::LuaNativeAsyncFn,
crate::types::AsyncCallback,
std::future::{self, Future},
};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Function(pub(crate) ValueRef);
/// Contains information about a function.
@@ -75,7 +77,7 @@ impl Function {
///
/// let tostring: Function = globals.get("tostring")?;
///
/// assert_eq!(tostring.call::<_, String>(123)?, "123");
/// assert_eq!(tostring.call::<String>(123)?, "123");
///
/// # Ok(())
/// # }
@@ -94,12 +96,12 @@ impl Function {
/// end
/// "#).eval()?;
///
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
/// assert_eq!(sum.call::<u32>((3, 4))?, 3 + 4);
///
/// # Ok(())
/// # }
/// ```
pub fn call<A: IntoLuaMulti, R: FromLuaMulti>(&self, args: A) -> Result<R> {
pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -153,9 +155,8 @@ impl Function {
/// [`AsyncThread`]: crate::AsyncThread
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
@@ -188,15 +189,15 @@ impl Function {
/// "#).eval()?;
///
/// let bound_a = sum.bind(1)?;
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
/// assert_eq!(bound_a.call::<u32>(2)?, 1 + 2);
///
/// let bound_a_and_b = sum.bind(13)?.bind(57)?;
/// assert_eq!(bound_a_and_b.call::<_, u32>(())?, 13 + 57);
/// assert_eq!(bound_a_and_b.call::<u32>(())?, 13 + 57);
///
/// # Ok(())
/// # }
/// ```
pub fn bind<A: IntoLuaMulti>(&self, args: A) -> Result<Function> {
pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
@@ -388,9 +389,9 @@ impl Function {
/// 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.
/// For Luau a [`Compiler`] can be used to compile Lua chunks to bytecode.
///
/// [Compiler]: crate::chunk::Compiler
/// [`Compiler`]: crate::chunk::Compiler
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn dump(&self, strip: bool) -> Vec<u8> {
@@ -489,10 +490,10 @@ impl Function {
///
/// Copies the function prototype and all its upvalues to the
/// newly created function.
///
/// This function returns shallow clone (same handle) for Rust/C functions.
///
/// Requires `feature = "luau"`
#[cfg(feature = "luau")]
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn deep_clone(&self) -> Self {
let lua = self.0.lua.lock();
@@ -508,46 +509,74 @@ impl Function {
}
}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
pub(crate) struct WrappedFunction(pub(crate) Callback<'static>);
pub(crate) struct WrappedFunction(pub(crate) Callback);
#[cfg(feature = "async")]
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback<'static>);
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback);
impl Function {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
#[inline]
pub fn wrap<A, R, F>(func: F) -> impl IntoLua
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua.lua(), args)?.push_into_stack_multi(lua)
func.call(args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua.lua(), args)?.push_into_stack_multi(lua)
func.call(args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
///
/// This function is similar to [`Function::wrap`] but any returned `Result` will be converted
/// to a `ok, err` tuple without throwing an exception.
#[inline]
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeFn<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args).push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
///
/// This function is similar to [`Function::wrap_mut`] but any returned `Result` will be
/// converted to a `ok, err` tuple without throwing an exception.
#[inline]
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeFnMut<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args).push_into_stack_multi(lua)
}))
}
@@ -555,22 +584,43 @@ impl Function {
/// trait.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: Fn(&Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + 'static,
{
WrappedAsyncFunction(Box::new(move |rawlua, args| unsafe {
let lua = rawlua.lua();
let args = match A::from_lua_args(args, 1, None, lua) {
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = func(lua, args);
let weak = rawlua.weak().clone();
Box::pin(async move { fut.await?.push_into_stack_multi(&weak.lock()) })
let lua = rawlua.lua();
let fut = func.call(args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
///
/// This function is similar to [`Function::wrap_async`] but any returned `Result` will be
/// converted to a `ok, err` tuple without throwing an exception.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let lua = rawlua.lua();
let fut = func.call(args);
Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
}))
}
}
@@ -590,6 +640,10 @@ impl IntoLua for WrappedAsyncFunction {
}
}
impl LuaType for Function {
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
}
#[cfg(test)]
mod assertions {
use super::*;
+14 -9
View File
@@ -16,9 +16,9 @@ use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
/// 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].
/// found in the Lua [documentation].
///
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
/// [`Lua::set_hook`]: crate::Lua::set_hook
pub struct Debug<'a> {
lua: EitherLua<'a>,
@@ -66,7 +66,7 @@ impl<'a> Debug<'a> {
/// Returns the specific event that triggered the hook.
///
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
/// 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
@@ -184,8 +184,8 @@ impl<'a> Debug<'a> {
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
"lua_getinfo failed with `a`"
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
"lua_getinfo failed with `au`"
);
#[cfg(not(feature = "luau"))]
@@ -198,8 +198,8 @@ impl<'a> Debug<'a> {
};
#[cfg(feature = "luau")]
let stack = DebugStack {
num_ups: (*self.ar.get()).nupvals as i32,
num_params: (*self.ar.get()).nparams as i32,
num_ups: (*self.ar.get()).nupvals,
num_params: (*self.ar.get()).nparams,
is_vararg: (*self.ar.get()).isvararg != 0,
};
stack
@@ -262,10 +262,15 @@ pub struct DebugSource<'a> {
#[derive(Copy, Clone, Debug)]
pub struct DebugStack {
pub num_ups: i32,
/// Number of upvalues.
pub num_ups: u8,
/// Number of parameters.
///
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub num_params: i32,
pub num_params: u8,
/// Whether the function is a vararg function.
///
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub is_vararg: bool,
+37 -41
View File
@@ -32,53 +32,45 @@
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
//!
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
//! (including [`UserData`]) into Rust values.
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
//! Lua values into Rust values.
//!
//! Requires `feature = "serialize"`.
//!
//! # Async/await support
//!
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or
//! polling [`AsyncThread`] using any runtime (eg. Tokio).
//! The [`Lua::create_async_function`] allows creating non-blocking functions that returns
//! [`Future`]. Lua code with async capabilities can be executed by [`Function::call_async`] family
//! of functions or polling [`AsyncThread`] using any runtime (eg. Tokio).
//!
//! Requires `feature = "async"`.
//!
//! # `Send` requirement
//! # `Send` and `Sync` support
//!
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
//! `Send` requirement to [`Function`]s and [`UserData`].
//! `Send` requirement to Rust functions and [`UserData`] types.
//!
//! In this case [`Lua`] object and their types can be send or used from other threads. Internally
//! access to Lua VM is synchronized using a reentrant mutex that can be locked many times within
//! the same thread.
//!
//! [Lua programming language]: https://www.lua.org/
//! [`Lua`]: crate::Lua
//! [executing]: crate::Chunk::exec
//! [evaluating]: crate::Chunk::eval
//! [globals]: crate::Lua::globals
//! [`IntoLua`]: crate::IntoLua
//! [`FromLua`]: crate::FromLua
//! [`IntoLuaMulti`]: crate::IntoLuaMulti
//! [`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
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(warn(warnings))))] // FIXME: Remove this when rust-lang/rust#123748 is fixed
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
#[macro_use]
mod macros;
mod buffer;
mod chunk;
mod conversion;
mod error;
@@ -88,52 +80,57 @@ mod hook;
mod luau;
mod memory;
mod multi;
// mod scope;
mod scope;
mod state;
mod stdlib;
mod string;
mod table;
mod thread;
mod traits;
mod types;
mod userdata;
mod util;
mod value;
mod vector;
pub mod prelude;
pub use bstr::BString;
pub use ffi::{self, lua_CFunction, lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::multi::Variadic;
pub use crate::multi::{MultiValue, Variadic};
pub use crate::scope::Scope;
pub use crate::state::{GCMode, Lua, LuaOptions};
// pub use crate::scope::Scope;
pub use crate::stdlib::StdLib;
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
pub use crate::table::{Table, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{
AnyUserData, AnyUserDataExt, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
UserDataRef, UserDataRefMut, UserDataRegistry,
pub use crate::traits::{
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
};
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
pub use crate::types::{
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
UserDataRefMut, UserDataRegistry,
};
pub use crate::value::{Nil, Value};
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{
chunk::Compiler,
function::CoverageInfo,
types::{Vector, VmState},
};
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, vector::Vector};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
#[cfg(feature = "serialize")]
#[doc(inline)]
@@ -196,10 +193,6 @@ extern crate mlua_derive;
/// - The `//` (floor division) operator is unusable, as its start a comment.
///
/// Everything else should work.
///
/// [`AsChunk`]: crate::AsChunk
/// [`UserData`]: crate::UserData
/// [`IntoLua`]: crate::IntoLua
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::chunk;
@@ -257,6 +250,9 @@ pub use mlua_derive::FromLua;
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
#[cfg(all(feature = "module", feature = "send"))]
compile_error!("`send` feature is not supported in module mode");
pub(crate) mod private {
use super::*;
+1 -17
View File
@@ -1,5 +1,5 @@
use std::ffi::CStr;
use std::os::raw::{c_float, c_int};
use std::os::raw::c_int;
use crate::error::Result;
use crate::state::Lua;
@@ -11,7 +11,6 @@ impl Lua {
let globals = self.globals();
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
// Set `_VERSION` global to include version number
// The environment variable `LUAU_VERSION` set by the build script
@@ -65,21 +64,6 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
}
}
// Luau vector datatype constructor
unsafe extern "C-unwind" 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;
#[cfg(feature = "luau-vector4")]
let w = ffi::luaL_checknumber(state, 4) as c_float;
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, x, y, z);
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, x, y, z, w);
1
}
pub(crate) use package::register_package_module;
mod package;
+23 -20
View File
@@ -9,8 +9,8 @@ use crate::chunk::ChunkMode;
use crate::error::Result;
use crate::state::Lua;
use crate::table::Table;
use crate::types::RegistryKey;
use crate::value::{IntoLua, Value};
use crate::traits::IntoLua;
use crate::value::Value;
#[cfg(unix)]
use {libloading::Library, rustc_hash::FxHashMap};
@@ -20,16 +20,13 @@ use {libloading::Library, rustc_hash::FxHashMap};
//
#[cfg(unix)]
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 1;
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 2;
#[cfg(all(unix, feature = "module"))]
#[no_mangle]
#[used]
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
// We keep reference to the `package` table in registry under this key
struct PackageKey(RegistryKey);
// We keep reference to the loaded dylibs in application data
#[cfg(unix)]
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
@@ -51,9 +48,8 @@ impl std::ops::DerefMut for LoadedDylibs {
}
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
// Create the package table and store it in app_data for later use (bypassing globals lookup)
// Create the package table
let package = lua.create_table()?;
lua.set_app_data(PackageKey(lua.create_registry_value(&package)?));
// Set `package.path`
let mut search_path = env::var("LUAU_PATH")
@@ -81,9 +77,15 @@ pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
}
// Set `package.loaded` (table with a list of loaded modules)
let loaded = lua.create_table()?;
package.raw_set("loaded", &loaded)?;
lua.set_named_registry_value("_LOADED", loaded)?;
let loaded = if let Ok(Some(loaded)) = lua.named_registry_value::<Option<Table>>("_LOADED") {
package.raw_set("loaded", &loaded)?;
loaded
} else {
let loaded = lua.create_table()?;
package.raw_set("loaded", &loaded)?;
lua.set_named_registry_value("_LOADED", &loaded)?;
loaded
};
// Set `package.loaders`
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
@@ -97,7 +99,8 @@ pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
// Register the module and `require` function in globals
let globals = lua.globals();
globals.raw_set("package", package)?;
globals.raw_set("package", &package)?;
loaded.raw_set("package", package)?;
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
Ok(())
@@ -191,17 +194,17 @@ fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option
/// Tries to load a lua (text) file
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
let package = {
let key = lua.app_data_ref::<PackageKey>().unwrap();
lua.registry_value::<Table>(&key.0)
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
loaded.raw_get::<Table>("package")
}?;
let search_path = package.get::<_, StdString>("path").unwrap_or_default();
let search_path = package.get::<StdString>("path").unwrap_or_default();
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
match fs::read(&file_path) {
Ok(buf) => {
return lua
.load(&buf)
.set_name(&format!("={}", file_path.display()))
.load(buf)
.set_name(format!("={}", file_path.display()))
.set_mode(ChunkMode::Text)
.into_function()
.map(Value::Function);
@@ -219,10 +222,10 @@ fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
#[cfg(unix)]
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
let package = {
let key = lua.app_data_ref::<PackageKey>().unwrap();
lua.registry_value::<Table>(&key.0)
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
loaded.raw_get::<Table>("package")
}?;
let search_cpath = package.get::<_, StdString>("cpath").unwrap_or_default();
let search_cpath = package.get::<StdString>("cpath").unwrap_or_default();
let find_symbol = |lib: &Library| unsafe {
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes()) {
+157 -34
View File
@@ -1,14 +1,17 @@
use std::collections::{vec_deque, VecDeque};
use std::iter::FromIterator;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::result::Result as StdResult;
use crate::error::Result;
use crate::state::{Lua, RawLua};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::util::check_stack;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
use crate::value::{Nil, Value};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// Result is convertible to [`MultiValue`] following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
#[inline]
@@ -32,7 +35,7 @@ impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
match self {
Ok(_) => Ok(MultiValue::new()),
Ok(_) => const { Ok(MultiValue::new()) },
Err(err) => (Nil, err).into_lua_multi(lua),
}
}
@@ -49,7 +52,7 @@ impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
impl<T: IntoLua> IntoLuaMulti for T {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
let mut v = MultiValue::with_capacity(1);
v.push_back(self.into_lua(lua)?);
Ok(v)
}
@@ -89,6 +92,111 @@ impl<T: FromLua> FromLuaMulti for T {
}
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Default, Debug, Clone)]
pub struct MultiValue(VecDeque<Value>);
impl Deref for MultiValue {
type Target = VecDeque<Value>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MultiValue {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl MultiValue {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub const fn new() -> MultiValue {
MultiValue(VecDeque::new())
}
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
pub fn with_capacity(capacity: usize) -> MultiValue {
MultiValue(VecDeque::with_capacity(capacity))
}
/// Creates a `MultiValue` container from vector of values.
///
/// This methods needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
/// beginning of the allocation.
#[inline]
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
vec.into()
}
/// Consumes the `MultiValue` and returns a vector of values.
///
/// This methods works in *O*(1) time and does not allocate any additional memory.
#[inline]
pub fn into_vec(self) -> Vec<Value> {
self.into()
}
#[inline]
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
let iter = iter.into_iter();
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
for value in iter {
multi_value.push_back(value.into_lua(lua)?);
}
Ok(multi_value)
}
}
impl From<Vec<Value>> for MultiValue {
#[inline]
fn from(value: Vec<Value>) -> Self {
MultiValue(value.into())
}
}
impl From<MultiValue> for Vec<Value> {
#[inline]
fn from(value: MultiValue) -> Self {
value.0.into()
}
}
impl FromIterator<Value> for MultiValue {
#[inline]
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let mut multi_value = MultiValue::new();
multi_value.extend(iter);
multi_value
}
}
impl IntoIterator for MultiValue {
type Item = Value;
type IntoIter = vec_deque::IntoIter<Value>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let deque = mem::take(&mut self.0);
mem::forget(self);
deque.into_iter()
}
}
impl<'a> IntoIterator for &'a MultiValue {
type Item = &'a Value;
type IntoIter = vec_deque::Iter<'a, Value>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoLuaMulti for MultiValue {
#[inline]
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
@@ -126,10 +234,7 @@ impl FromLuaMulti for MultiValue {
/// # Ok(())
/// # }
/// ```
///
/// [`FromLua`]: crate::FromLua
/// [`MultiValue`]: crate::MultiValue
#[derive(Debug, Clone)]
#[derive(Default, Debug, Clone)]
pub struct Variadic<T>(Vec<T>);
impl<T> Variadic<T> {
@@ -137,11 +242,38 @@ impl<T> Variadic<T> {
pub const fn new() -> Variadic<T> {
Variadic(Vec::new())
}
/// Creates an empty `Variadic` container with space for at least `capacity` elements.
pub fn with_capacity(capacity: usize) -> Variadic<T> {
Variadic(Vec::with_capacity(capacity))
}
}
impl<T> Default for Variadic<T> {
fn default() -> Variadic<T> {
Variadic::new()
impl<T> Deref for Variadic<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Variadic<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<Vec<T>> for Variadic<T> {
#[inline]
fn from(vec: Vec<T>) -> Self {
Variadic(vec)
}
}
impl<T> From<Variadic<T>> for Vec<T> {
#[inline]
fn from(value: Variadic<T>) -> Self {
value.0
}
}
@@ -160,26 +292,10 @@ impl<T> IntoIterator for Variadic<T> {
}
}
impl<T> Deref for Variadic<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Variadic<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
values.extend_from_values(self.0.into_iter().map(|val| val.into_lua(lua)))?;
Ok(values)
MultiValue::from_lua_iter(lua, self)
}
}
@@ -198,8 +314,8 @@ macro_rules! impl_tuple {
() => (
impl IntoLuaMulti for () {
#[inline]
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
Ok(MultiValue::with_lua_and_capacity(lua, 0))
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
const { Ok(MultiValue::new()) }
}
#[inline]
@@ -215,10 +331,7 @@ macro_rules! impl_tuple {
}
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
if nvals > 0 {
ffi::lua_pop(lua.state(), nvals);
}
unsafe fn from_stack_multi(_nvals: c_int, _lua: &RawLua) -> Result<Self> {
Ok(())
}
}
@@ -344,3 +457,13 @@ impl_tuple!(A B C D E F G H I J K L M);
impl_tuple!(A B C D E F G H I J K L M N);
impl_tuple!(A B C D E F G H I J K L M N O);
impl_tuple!(A B C D E F G H I J K L M N O P);
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(MultiValue: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
}
+8 -7
View File
@@ -2,17 +2,18 @@
#[doc(no_inline)]
pub use crate::{
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk, Error as LuaError,
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Either as LuaEither, Error as LuaError,
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs,
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn,
LuaNativeFnMut, LuaOptions, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil,
Number as LuaNumber, ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult,
StdLib as LuaStdLib, String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs,
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
VmState as LuaVmState,
};
#[cfg(not(feature = "luau"))]
@@ -21,11 +22,11 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector};
#[cfg(feature = "async")]
#[doc(no_inline)]
pub use crate::AsyncThread as LuaAsyncThread;
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
#[cfg(feature = "serialize")]
#[doc(no_inline)]
+203 -818
View File
File diff suppressed because it is too large Load Diff
+49 -31
View File
@@ -1,3 +1,5 @@
//! Deserialize Lua values to a Rust data structure.
use std::cell::RefCell;
use std::os::raw::c_void;
use std::rc::Rc;
@@ -51,7 +53,7 @@ pub struct Options {
impl Default for Options {
fn default() -> Self {
Self::new()
const { Self::new() }
}
}
@@ -94,12 +96,12 @@ impl Options {
}
impl Deserializer {
/// Creates a new Lua Deserializer for the `Value`.
/// Creates a new Lua Deserializer for the [`Value`].
pub fn new(value: Value) -> Self {
Self::new_with_options(value, Options::default())
}
/// Creates a new Lua Deserializer for the `Value` with custom options.
/// Creates a new Lua Deserializer for the [`Value`] with custom options.
pub fn new_with_options(value: Value, options: Options) -> Self {
Deserializer {
value,
@@ -145,19 +147,13 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
serde_userdata(ud, |value| value.deserialize_any(visitor))
}
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let lua = ud.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
let buf = std::slice::from_raw_parts(buf as *const u8, size);
visitor.visit_bytes(buf)
},
Value::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
| Value::Error(_)
| Value::Other(_) => {
if self.options.deny_unsupported_types {
let msg = format!("unsupported value type `{}`", self.value.type_name());
Err(de::Error::custom(msg))
@@ -424,7 +420,7 @@ impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
#[cfg(feature = "luau")]
struct VecDeserializer {
vec: crate::types::Vector,
vec: crate::Vector,
next: usize,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
@@ -450,7 +446,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
}
fn size_hint(&self) -> Option<usize> {
Some(crate::types::Vector::SIZE)
Some(crate::Vector::SIZE)
}
}
@@ -463,7 +459,7 @@ impl<'a> MapPairs<'a> {
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
if sort_keys {
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
pairs.sort_by(|(a, _), (b, _)| b.sort_cmp(a)); // reverse order as we pop values from the end
Ok(MapPairs::Vec(pairs))
} else {
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
@@ -504,13 +500,8 @@ struct MapDeserializer<'a> {
processed: usize,
}
impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
type Error = Error;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
impl MapDeserializer<'_> {
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
loop {
match self.pairs.next() {
Some(item) => {
@@ -526,23 +517,45 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
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);
return Ok(Some(key_de));
}
None => return Ok(None),
}
}
}
fn next_value_deserializer(&mut self) -> Result<Deserializer> {
match self.value.take() {
Some(value) => {
let visited = Rc::clone(&self.visited);
Ok(Deserializer::from_parts(value, self.options, visited))
}
None => Err(de::Error::custom("value is missing")),
}
}
}
impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
type Error = Error;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
match self.next_key_deserializer() {
Ok(Some(key_de)) => seed.deserialize(key_de).map(Some),
Ok(None) => Ok(None),
Err(error) => Err(error),
}
}
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")),
match self.next_value_deserializer() {
Ok(value_de) => seed.deserialize(value_de),
Err(error) => Err(error),
}
}
@@ -702,6 +715,11 @@ fn serde_userdata<V>(
ud: AnyUserData,
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
) -> Result<V> {
let value = serde_value::to_value(ud).map_err(|err| Error::SerializeError(err.to_string()))?;
f(value).map_err(|err| Error::DeserializeError(err.to_string()))
match serde_value::to_value(ud) {
Ok(value) => match f(value) {
Ok(r) => Ok(r),
Err(error) => Err(Error::DeserializeError(error.to_string())),
},
Err(error) => Err(Error::SerializeError(error.to_string())),
}
}
-6
View File
@@ -106,8 +106,6 @@ pub trait LuaSerdeExt: Sealed {
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # Example
///
/// ```
@@ -133,8 +131,6 @@ pub trait LuaSerdeExt: Sealed {
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # Example
///
/// ```
@@ -164,8 +160,6 @@ pub trait LuaSerdeExt: Sealed {
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: crate::Value
///
/// # Example
///
/// ```
+8 -5
View File
@@ -1,10 +1,13 @@
//! Serialize a Rust data structure into Lua value.
use serde::{ser, Serialize};
use super::LuaSerdeExt;
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::table::Table;
use crate::value::{IntoLua, Value};
use crate::traits::IntoLua;
use crate::value::Value;
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
@@ -52,7 +55,7 @@ pub struct Options {
impl Default for Options {
fn default() -> Self {
Self::new()
const { Self::new() }
}
}
@@ -266,7 +269,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
#[inline]
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
#[cfg(feature = "luau")]
if name == "Vector" && len == crate::types::Vector::SIZE {
if name == "Vector" && len == crate::Vector::SIZE {
return Ok(SerializeSeq::new_vector(self.lua, self.options));
}
_ = name;
@@ -340,7 +343,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
pub struct SerializeSeq<'a> {
lua: &'a Lua,
#[cfg(feature = "luau")]
vector: Option<crate::types::Vector>,
vector: Option<crate::Vector>,
table: Option<Table>,
next: usize,
options: Options,
@@ -362,7 +365,7 @@ impl<'a> SerializeSeq<'a> {
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
Self {
lua,
vector: Some(crate::types::Vector::zero()),
vector: Some(crate::Vector::zero()),
table: None,
next: 0,
options,
+357 -262
View File
File diff suppressed because it is too large Load Diff
+26 -27
View File
@@ -1,10 +1,9 @@
use std::any::TypeId;
use std::cell::UnsafeCell;
use std::rc::Rc;
// use std::collections::VecDeque;
use std::mem::{self, MaybeUninit};
use std::mem::MaybeUninit;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::rc::Rc;
use std::sync::Arc;
use parking_lot::Mutex;
@@ -13,7 +12,7 @@ use rustc_hash::FxHashMap;
use crate::error::Result;
use crate::state::RawLua;
use crate::stdlib::StdLib;
use crate::types::{AppData, ReentrantMutex, XRc, XWeak};
use crate::types::{AppData, ReentrantMutex, XRc};
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
#[cfg(any(feature = "luau", doc))]
@@ -28,17 +27,15 @@ use super::{Lua, WeakLua};
static EXTRA_REGISTRY_KEY: u8 = 0;
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
// const MULTIVALUE_POOL_SIZE: usize = 64;
const REF_STACK_RESERVE: c_int = 1;
/// Data associated with the Lua state.
pub(crate) struct ExtraData {
// Same layout as `Lua`
pub(super) lua: MaybeUninit<XRc<ReentrantMutex<RawLua>>>,
// Same layout as `WeakLua`
pub(super) weak: MaybeUninit<XWeak<ReentrantMutex<RawLua>>>,
pub(super) lua: MaybeUninit<Lua>,
pub(super) weak: MaybeUninit<WeakLua>,
pub(super) owned: bool,
pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
@@ -50,7 +47,7 @@ pub(crate) struct ExtraData {
pub(super) safe: bool,
pub(super) libs: StdLib,
#[cfg(feature = "module")]
// Used in module mode
pub(super) skip_memory_check: bool,
// Auxiliary thread to store references
@@ -61,8 +58,6 @@ pub(crate) struct ExtraData {
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
pub(super) wrapped_failure_pool: Vec<c_int>,
// Pool of `MultiValue` containers
// multivalue_pool: Vec<VecDeque<Value>>,
// Pool of `Thread`s (coroutines) for async execution
#[cfg(feature = "async")]
pub(super) thread_pool: Vec<c_int>,
@@ -94,8 +89,9 @@ pub(crate) struct ExtraData {
impl Drop for ExtraData {
fn drop(&mut self) {
unsafe {
#[cfg(feature = "module")]
self.lua.assume_init_drop();
if !self.owned {
self.lua.assume_init_drop();
}
self.weak.assume_init_drop();
}
@@ -117,7 +113,7 @@ impl ExtraData {
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
pub(super) const ERROR_TRACEBACK_IDX: c_int = 1;
pub(super) unsafe fn init(state: *mut ffi::lua_State) -> XRc<UnsafeCell<Self>> {
pub(super) unsafe fn init(state: *mut ffi::lua_State, owned: bool) -> XRc<UnsafeCell<Self>> {
// Create ref stack thread and place it in the registry to prevent it
// from being garbage collected.
let ref_thread = mlua_expect!(
@@ -143,17 +139,18 @@ impl ExtraData {
assert_eq!(ffi::lua_gettop(ref_thread), Self::ERROR_TRACEBACK_IDX);
}
#[allow(clippy::arc_with_non_send_sync)]
let extra = XRc::new(UnsafeCell::new(ExtraData {
lua: MaybeUninit::uninit(),
weak: MaybeUninit::uninit(),
registered_userdata: FxHashMap::default(),
owned,
registered_userdata_t: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
app_data: AppData::default(),
safe: false,
libs: StdLib::NONE,
#[cfg(feature = "module")]
skip_memory_check: false,
ref_thread,
// We need some reserved stack space to move values in and out of the ref stack.
@@ -161,7 +158,6 @@ impl ExtraData {
ref_stack_top: ffi::lua_gettop(ref_thread),
ref_free: Vec::new(),
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
// multivalue_pool: Vec::with_capacity(MULTIVALUE_POOL_SIZE),
#[cfg(feature = "async")]
thread_pool: Vec::new(),
wrapped_failure_mt_ptr,
@@ -189,12 +185,15 @@ impl ExtraData {
extra
}
pub(super) unsafe fn set_lua(&mut self, lua: &XRc<ReentrantMutex<RawLua>>) {
self.lua.write(XRc::clone(lua));
if cfg!(not(feature = "module")) {
XRc::decrement_strong_count(XRc::as_ptr(lua));
pub(super) unsafe fn set_lua(&mut self, raw: &XRc<ReentrantMutex<RawLua>>) {
self.lua.write(Lua {
raw: XRc::clone(raw),
collect_garbage: false,
});
if self.owned {
XRc::decrement_strong_count(XRc::as_ptr(raw));
}
self.weak.write(XRc::downgrade(lua));
self.weak.write(WeakLua(XRc::downgrade(raw)));
}
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
@@ -232,16 +231,16 @@ impl ExtraData {
#[inline(always)]
pub(super) unsafe fn lua(&self) -> &Lua {
mem::transmute(self.lua.assume_init_ref())
self.lua.assume_init_ref()
}
#[inline(always)]
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
&*self.lua.assume_init_ref().data_ptr()
&*self.lua.assume_init_ref().raw.data_ptr()
}
#[inline(always)]
pub(super) unsafe fn weak(&self) -> &WeakLua {
mem::transmute(self.weak.assume_init_ref())
self.weak.assume_init_ref()
}
}
+215 -207
View File
@@ -1,12 +1,12 @@
use std::any::TypeId;
use std::cell::{Cell, UnsafeCell};
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::resume_unwind;
use std::rc::Rc;
use std::ptr::{self, NonNull};
use std::result::Result as StdResult;
use std::sync::Arc;
use std::{mem, ptr};
use crate::chunk::ChunkMode;
use crate::error::{Error, Result};
@@ -17,18 +17,19 @@ use crate::stdlib::StdLib;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::traits::IntoLua;
use crate::types::{
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
MaybeSend, ReentrantMutex, RegistryKey, SubtypeId, ValueRef, XRc,
MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataVariant};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
use crate::util::{
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
StackGuard, WrappedFailure,
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable,
pop_error, push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall,
short_type_name, take_userdata, StackGuard, WrappedFailure,
};
use crate::value::{FromLuaMulti, IntoLua, MultiValue, Nil, Value};
use crate::value::{Nil, Value};
use super::extra::ExtraData;
use super::{Lua, LuaOptions, WeakLua};
@@ -38,26 +39,31 @@ use crate::hook::{Debug, HookTriggers};
#[cfg(feature = "async")]
use {
crate::multi::MultiValue,
crate::traits::FromLuaMulti,
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
std::ptr::NonNull,
std::task::{Context, Poll, Waker},
};
/// An inner Lua struct which holds a raw Lua state.
#[doc(hidden)]
pub struct RawLua {
// The state is dynamic and depends on context
pub(super) state: Cell<*mut ffi::lua_State>,
pub(super) main_state: *mut ffi::lua_State,
pub(super) main_state: Option<NonNull<ffi::lua_State>>,
pub(super) extra: XRc<UnsafeCell<ExtraData>>,
}
#[cfg(not(feature = "module"))]
impl Drop for RawLua {
fn drop(&mut self) {
unsafe {
let mem_state = MemoryState::get(self.main_state);
if !(*self.extra.get()).owned {
return;
}
ffi::lua_close(self.main_state);
let mem_state = MemoryState::get(self.main_state());
ffi::lua_close(self.main_state());
// Deallocate `MemoryState`
if !mem_state.is_null() {
@@ -81,15 +87,19 @@ impl RawLua {
unsafe { (*self.extra.get()).weak() }
}
/// Returns a pointer to the current Lua state.
///
/// The pointer refers to the active Lua coroutine and depends on the context.
#[inline(always)]
pub(crate) fn state(&self) -> *mut ffi::lua_State {
pub fn state(&self) -> *mut ffi::lua_State {
self.state.get()
}
#[cfg(feature = "luau")]
#[inline(always)]
pub(crate) fn main_state(&self) -> *mut ffi::lua_State {
self.main_state
.map(|state| state.as_ptr())
.unwrap_or_else(|| self.state())
}
#[inline(always)]
@@ -116,11 +126,11 @@ impl RawLua {
ffi::luau_codegen_create(state);
}
let rawlua = Self::init_from_ptr(state);
let rawlua = Self::init_from_ptr(state, true);
let extra = rawlua.lock().extra.get();
mlua_expect!(
load_from_std_lib(state, libs),
load_std_libs(state, libs),
"Error during loading standard libraries"
);
(*extra).libs |= libs;
@@ -155,7 +165,7 @@ impl RawLua {
rawlua
}
pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> XRc<ReentrantMutex<Self>> {
pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State, owned: bool) -> XRc<ReentrantMutex<Self>> {
assert!(!state.is_null(), "Lua state is NULL");
if let Some(lua) = Self::try_from_ptr(state) {
return lua;
@@ -192,7 +202,7 @@ impl RawLua {
);
// Init ExtraData
let extra = ExtraData::init(main_state);
let extra = ExtraData::init(main_state, owned);
// Register `DestructedUserdata` type
get_destructed_userdata_metatable(main_state);
@@ -212,7 +222,8 @@ impl RawLua {
#[allow(clippy::arc_with_non_send_sync)]
let rawlua = XRc::new(ReentrantMutex::new(RawLua {
state: Cell::new(state),
main_state,
// Make sure that we don't store current state as main state (if it's not available)
main_state: get_main_state(state).and_then(NonNull::new),
extra: XRc::clone(&extra),
}));
(*extra.get()).set_lua(&rawlua);
@@ -220,17 +231,17 @@ impl RawLua {
rawlua
}
pub(super) unsafe fn try_from_ptr(state: *mut ffi::lua_State) -> Option<XRc<ReentrantMutex<Self>>> {
unsafe fn try_from_ptr(state: *mut ffi::lua_State) -> Option<XRc<ReentrantMutex<Self>>> {
match ExtraData::get(state) {
extra if extra.is_null() => None,
extra => Some(XRc::clone(&(*extra).lua().0)),
extra => Some(XRc::clone(&(*extra).lua().raw)),
}
}
/// Marks the Lua state as safe.
#[inline(always)]
pub(super) unsafe fn set_safe(&self) {
(*self.extra.get()).safe = true;
pub(super) fn mark_safe(&self) {
unsafe { (*self.extra.get()).safe = true };
}
/// Loads the specified subset of the standard libraries into an existing Lua state.
@@ -254,7 +265,7 @@ impl RawLua {
));
}
let res = load_from_std_lib(self.main_state, libs);
let res = load_std_libs(self.main_state(), libs);
// If `package` library loaded into a safe lua state then disable C modules
let curr_libs = (*self.extra.get()).libs;
@@ -276,7 +287,7 @@ impl RawLua {
/// See [`Lua::app_data_ref`]
#[track_caller]
#[inline]
pub(crate) fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
pub(crate) fn app_data_ref_unguarded<T: 'static>(&self) -> Option<AppDataRef<T>> {
let extra = unsafe { &*self.extra.get() };
extra.app_data.borrow(None)
}
@@ -284,7 +295,7 @@ impl RawLua {
/// See [`Lua::app_data_mut`]
#[track_caller]
#[inline]
pub(crate) fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
pub(crate) fn app_data_mut_unguarded<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
let extra = unsafe { &*self.extra.get() };
extra.app_data.borrow_mut(None)
}
@@ -299,14 +310,14 @@ impl RawLua {
pub(crate) fn load_chunk(
&self,
name: Option<&CStr>,
env: Option<Table>,
env: Option<&Table>,
mode: Option<ChunkMode>,
source: &[u8],
) -> Result<Function> {
let state = self.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 1)?;
check_stack(state, 2)?;
let mode_str = match mode {
Some(ChunkMode::Binary) => cstr!("b"),
@@ -314,22 +325,21 @@ impl RawLua {
None => cstr!("bt"),
};
match ffi::luaL_loadbufferx(
match ffi::luaL_loadbufferenv(
state,
source.as_ptr() as *const c_char,
source.len(),
name.map(|n| n.as_ptr()).unwrap_or_else(ptr::null),
mode_str,
match env {
Some(env) => {
self.push_ref(&env.0);
-1
}
_ => 0,
},
) {
ffi::LUA_OK => {
if let Some(env) = env {
self.push_ref(&env.0);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_setupvalue(state, -2, 1);
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
ffi::lua_setfenv(state, -2);
}
#[cfg(feature = "luau-jit")]
if (*self.extra.get()).enable_jit && ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
@@ -350,8 +360,11 @@ impl RawLua {
triggers: HookTriggers,
callback: F,
) where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
F: Fn(&Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
{
use crate::types::VmState;
use std::rc::Rc;
unsafe extern "C-unwind" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) {
let extra = ExtraData::get(state);
if (*extra).hook_thread != state {
@@ -359,17 +372,34 @@ impl RawLua {
ffi::lua_sethook(state, None, 0, 0);
return;
}
callback_error_ext(state, extra, move |_| {
let result = callback_error_ext(state, extra, move |extra, _| {
let hook_cb = (*extra).hook_callback.clone();
let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc");
if Rc::strong_count(&hook_cb) > 2 {
return Ok(()); // Don't allow recursion
return Ok(VmState::Continue); // Don't allow recursion
}
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
let debug = Debug::new(rawlua, ar);
hook_cb((*extra).lua(), debug)
})
});
match result {
VmState::Continue => {}
VmState::Yield => {
// Only count and line events can yield
if (*ar).event == ffi::LUA_HOOKCOUNT || (*ar).event == ffi::LUA_HOOKLINE {
#[cfg(any(feature = "lua54", feature = "lua53"))]
if ffi::lua_isyieldable(state) != 0 {
ffi::lua_yield(state, 0);
}
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
{
ffi::lua_pushliteral(state, "attempt to yield from a hook");
ffi::lua_error(state);
}
}
}
}
}
(*self.extra.get()).hook_callback = Some(Rc::new(callback));
@@ -491,37 +521,17 @@ impl RawLua {
#[cfg(feature = "luau")]
ffi::lua_resetthread(thread_state);
extra.thread_pool.push(thread.0.index);
thread.0.drop = false;
thread.0.drop = false; // Prevent thread from being garbage collected
return true;
}
false
}
// FIXME
// #[inline]
// pub(crate) fn pop_multivalue_from_pool(&self) -> Option<VecDeque<Value>> {
// let extra = unsafe { &mut *self.extra.get() };
// extra.multivalue_pool.pop()
// }
// FIXME
// #[inline]
// pub(crate) fn push_multivalue_to_pool(&self, mut multivalue: VecDeque<Value>) {
// let extra = unsafe { &mut *self.extra.get() };
// if extra.multivalue_pool.len() < MULTIVALUE_POOL_SIZE {
// multivalue.clear();
// extra
// .multivalue_pool
// .push(unsafe { mem::transmute(multivalue) });
// }
// }
/// Pushes a value that implements `IntoLua` onto the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
#[inline(always)]
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
pub(crate) unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
value.push_into_stack(self)
}
@@ -548,10 +558,13 @@ impl RawLua {
Value::Function(f) => self.push_ref(&f.0),
Value::Thread(t) => self.push_ref(&t.0),
Value::UserData(ud) => self.push_ref(&ud.0),
#[cfg(feature = "luau")]
Value::Buffer(buf) => self.push_ref(&buf.0),
Value::Error(err) => {
let protect = !self.unlikely_memory_error();
push_internal_userdata(state, WrappedFailure::Error(*err.clone()), protect)?;
}
Value::Other(vref) => self.push_ref(vref),
}
Ok(())
}
@@ -560,7 +573,7 @@ impl RawLua {
///
/// Uses 2 stack spaces, does not call `checkstack`.
pub(crate) unsafe fn pop_value(&self) -> Value {
let value = self.stack_value(-1);
let value = self.stack_value(-1, None);
ffi::lua_pop(self.state(), 1);
value
}
@@ -568,9 +581,9 @@ impl RawLua {
/// Returns value at given stack index without popping it.
///
/// Uses 2 stack spaces, does not call checkstack.
pub(crate) unsafe fn stack_value(&self, idx: c_int) -> Value {
pub(crate) unsafe fn stack_value(&self, idx: c_int, type_hint: Option<c_int>) -> Value {
let state = self.state();
match ffi::lua_type(state, idx) {
match type_hint.unwrap_or_else(|| ffi::lua_type(state, idx)) {
ffi::LUA_TNIL => Nil,
ffi::LUA_TBOOLEAN => Value::Boolean(ffi::lua_toboolean(state, idx) != 0),
@@ -602,9 +615,9 @@ impl RawLua {
let v = ffi::lua_tovector(state, idx);
mlua_debug_assert!(!v.is_null(), "vector is null");
#[cfg(not(feature = "luau-vector4"))]
return Value::Vector(crate::types::Vector([*v, *v.add(1), *v.add(2)]));
return Value::Vector(crate::Vector([*v, *v.add(1), *v.add(2)]));
#[cfg(feature = "luau-vector4")]
return Value::Vector(crate::types::Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
return Value::Vector(crate::Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
}
ffi::LUA_TSTRING => {
@@ -636,7 +649,7 @@ impl RawLua {
}
_ => {
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::None))
Value::UserData(AnyUserData(self.pop_ref_thread()))
}
}
}
@@ -649,19 +662,14 @@ impl RawLua {
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
// Buffer is represented as a userdata type
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer))
Value::Buffer(crate::Buffer(self.pop_ref_thread()))
}
#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
// CData is represented as a userdata type
_ => {
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::CData))
Value::Other(self.pop_ref_thread())
}
_ => mlua_panic!("unexpected value type on stack"),
}
}
@@ -722,55 +730,57 @@ impl RawLua {
#[inline]
pub(crate) unsafe fn unlikely_memory_error(&self) -> bool {
#[cfg(debug_assertions)]
if cfg!(force_memory_limit) {
return false;
}
// MemoryInfo is empty in module mode so we cannot predict memory limits
match MemoryState::get(self.main_state) {
match MemoryState::get(self.state()) {
mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0,
#[cfg(feature = "module")]
_ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode)
#[cfg(not(feature = "module"))]
_ => false,
}
}
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
where
T: UserData + 'static,
{
self.make_userdata_with_metatable(data, || {
// Check if userdata/metatable is already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}
// Create a new metatable from `UserData` definition
let mut registry = UserDataRegistry::new();
let mut registry = UserDataRegistry::new(type_id);
T::register(&mut registry);
self.register_userdata_metatable(registry)
self.create_userdata_metatable(registry)
})
}
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
where
T: 'static,
{
self.make_userdata_with_metatable(data, || {
// Check if userdata/metatable is already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}
// Create an empty metatable
let registry = UserDataRegistry::new();
self.register_userdata_metatable::<T>(registry)
let registry = UserDataRegistry::<T>::new(type_id);
self.create_userdata_metatable(registry)
})
}
unsafe fn make_userdata_with_metatable<T>(
&self,
data: UserDataVariant<T>,
data: UserDataStorage<T>,
get_metatable_id: impl FnOnce() -> Result<Integer>,
) -> Result<AnyUserData> {
let state = self.state();
@@ -781,10 +791,7 @@ impl RawLua {
ffi::lua_pushnil(state);
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
let protect = !self.unlikely_memory_error();
#[cfg(not(feature = "lua54"))]
crate::util::push_userdata(state, data, protect)?;
#[cfg(feature = "lua54")]
crate::util::push_userdata_uv(state, data, crate::userdata::USER_VALUE_MAXSLOT as c_int, protect)?;
ffi::lua_replace(state, -3);
ffi::lua_setmetatable(state, -2);
@@ -800,15 +807,34 @@ impl RawLua {
ffi::lua_setuservalue(state, -2);
}
Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
Ok(AnyUserData(self.pop_ref()))
}
pub(crate) unsafe fn register_userdata_metatable<T: 'static>(
pub(crate) unsafe fn create_userdata_metatable<T>(
&self,
mut registry: UserDataRegistry<T>,
registry: UserDataRegistry<T>,
) -> Result<Integer> {
let state = self.state();
let _sg = StackGuard::new(state);
let type_id = registry.type_id();
self.push_userdata_metatable(registry)?;
let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
if let Some(type_id) = type_id {
(*self.extra.get()).registered_userdata_t.insert(type_id, id);
}
self.register_userdata_metatable(mt_ptr, type_id);
Ok(id as Integer)
}
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
let state = self.state();
let mut stack_guard = StackGuard::new(state);
check_stack(state, 13)?;
// Prepare metatable, add meta methods first and then meta fields
@@ -826,10 +852,9 @@ impl RawLua {
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
}
let mut has_name = false;
for (k, f) in registry.meta_fields {
for (k, push_field) in registry.meta_fields {
has_name = has_name || k == MetaMethod::Type;
let rawlua = mem::transmute::<&RawLua, &RawLua>(self);
mlua_assert!(f(rawlua, 0)? == 1, "field function must return one value");
push_field(self)?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
}
// Set `__name/__type` if not provided
@@ -840,8 +865,6 @@ impl RawLua {
}
let metatable_index = ffi::lua_absindex(state, -1);
let mut extra_tables_count = 0;
let fields_nrec = registry.fields.len();
if fields_nrec > 0 {
// If `__index` is a table then update it in-place
@@ -853,33 +876,39 @@ impl RawLua {
ffi::lua_pop(state, 1);
push_table(state, 0, fields_nrec, true)?;
}
for (k, f) in registry.fields {
let rawlua = mem::transmute::<&RawLua, &RawLua>(self);
mlua_assert!(f(rawlua, 0)? == 1, "field function must return one value");
for (k, push_field) in mem::take(&mut registry.fields) {
push_field(self)?;
rawset_field(state, -2, &k)?;
}
rawset_field(state, metatable_index, "__index")?;
}
_ => {
ffi::lua_pop(state, 1);
// Propagate fields to the field getters
for (k, f) in registry.fields {
registry.field_getters.push((k, f))
}
// Fields will be converted to functions and added to field getters
}
}
}
let mut field_getters_index = None;
let field_getters_nrec = registry.field_getters.len();
let field_getters_nrec = registry.field_getters.len() + registry.fields.len();
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec, true)?;
for (k, m) in registry.field_getters {
self.push(self.create_callback(m)?)?;
rawset_field(state, -2, &k)?;
}
for (k, push_field) in registry.fields {
unsafe extern "C-unwind" fn return_field(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
1
}
push_field(self)?;
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, return_field, 1);
})?;
rawset_field(state, -2, &k)?;
}
field_getters_index = Some(ffi::lua_absindex(state, -1));
extra_tables_count += 1;
}
let mut field_setters_index = None;
@@ -891,7 +920,6 @@ impl RawLua {
rawset_field(state, -2, &k)?;
}
field_setters_index = Some(ffi::lua_absindex(state, -1));
extra_tables_count += 1;
}
let mut methods_index = None;
@@ -928,18 +956,23 @@ impl RawLua {
}
_ => {
methods_index = Some(ffi::lua_absindex(state, -1));
extra_tables_count += 1;
}
}
}
#[cfg(feature = "luau")]
let extra_init = None;
#[cfg(not(feature = "luau"))]
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataVariant<T>>);
rawset_field(state, -2, "__gc")
});
unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
let ud = get_userdata::<UserDataStorage<T>>(state, -1);
if !(*ud).is_borrowed() {
take_userdata::<UserDataStorage<T>>(state);
ffi::lua_pushboolean(state, 1);
} else {
ffi::lua_pushboolean(state, 0);
}
1
}
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
rawset_field(state, metatable_index, "__gc")?;
init_userdata_metatable(
state,
@@ -947,50 +980,26 @@ impl RawLua {
field_getters_index,
field_setters_index,
methods_index,
extra_init,
)?;
// Pop extra tables to get metatable on top of the stack
ffi::lua_pop(state, extra_tables_count);
// Update stack guard to keep metatable after return
stack_guard.keep(1);
let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
let type_id = TypeId::of::<T>();
(*self.extra.get()).registered_userdata.insert(type_id, id);
(*self.extra.get())
.registered_userdata_mt
.insert(mt_ptr, Some(type_id));
Ok(id as Integer)
Ok(())
}
// #[inline]
// pub(crate) unsafe fn register_raw_userdata_metatable(
// &self,
// ptr: *const c_void,
// type_id: Option<TypeId>,
// ) {
// (*self.extra.get())
// .registered_userdata_mt
// .insert(ptr, type_id);
// }
#[inline(always)]
pub(crate) unsafe fn register_userdata_metatable(&self, mt_ptr: *const c_void, type_id: Option<TypeId>) {
(*self.extra.get()).registered_userdata_mt.insert(mt_ptr, type_id);
}
// #[inline]
// pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) {
// (*self.extra.get()).registered_userdata_mt.remove(&ptr);
// if (*self.extra.get()).last_checked_userdata_mt.0 == ptr {
// (*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
// }
// }
// #[inline(always)]
// pub(crate) unsafe fn get_userdata_ref<T: 'static>(&self, idx: c_int) -> Result<UserDataRef<T>> {
// let guard = self.lua().lock_arc();
// (*get_userdata::<UserDataVariant<T>>(self.state(), idx)).try_make_ref(guard)
// }
#[inline(always)]
pub(crate) unsafe fn deregister_userdata_metatable(&self, mt_ptr: *const c_void) {
(*self.extra.get()).registered_userdata_mt.remove(&mt_ptr);
if (*self.extra.get()).last_checked_userdata_mt.0 == mt_ptr {
(*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
}
}
// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
//
@@ -1000,8 +1009,18 @@ impl RawLua {
}
// Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
pub(crate) unsafe fn get_userdata_type_id(&self, idx: c_int) -> Result<Option<TypeId>> {
self.get_userdata_type_id_inner(self.state(), idx)
pub(crate) unsafe fn get_userdata_type_id<T>(&self, idx: c_int) -> Result<Option<TypeId>> {
match self.get_userdata_type_id_inner(self.state(), idx) {
Ok(type_id) => Ok(type_id),
Err(Error::UserDataTypeMismatch) if ffi::lua_type(self.state(), idx) != ffi::LUA_TUSERDATA => {
// Report `FromLuaConversionError` instead
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(self.state(), idx));
let idx_type_name = idx_type_name.to_str().unwrap();
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
}
Err(err) => Err(err),
}
}
unsafe fn get_userdata_type_id_inner(
@@ -1009,11 +1028,10 @@ impl RawLua {
state: *mut ffi::lua_State,
idx: c_int,
) -> Result<Option<TypeId>> {
if ffi::lua_getmetatable(state, idx) == 0 {
let mt_ptr = get_metatable_ptr(state, idx);
if mt_ptr.is_null() {
return Err(Error::UserDataTypeMismatch);
}
let mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
// Fast path to skip looking up the metatable in the map
let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt;
@@ -1044,27 +1062,16 @@ impl RawLua {
// Creates a Function out of a Callback containing a 'static Fn.
pub(crate) fn create_callback(&self, func: Callback) -> Result<Function> {
unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
// Normal functions can be scoped and therefore destroyed,
// so we need to check that the first upvalue is valid
let (upvalue, extra) = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
ffi::LUA_TUSERDATA => {
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
(upvalue, (*upvalue).extra.get())
}
_ => (ptr::null_mut(), ptr::null_mut()),
};
callback_error_ext(state, extra, |nargs| {
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
if upvalue.is_null() {
return Err(Error::CallbackDestructed);
}
// The lock must be already held as the callback is executed
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
let func = &*(*upvalue).data;
func(rawlua, nargs)
match (*upvalue).data {
Some(ref func) => func(rawlua, nargs),
None => Err(Error::CallbackDestructed),
}
})
}
@@ -1073,7 +1080,7 @@ impl RawLua {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let func = mem::transmute::<Callback, Callback<'static>>(func);
let func = Some(func);
let extra = XRc::clone(&self.extra);
let protect = !self.unlikely_memory_error();
push_internal_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
@@ -1091,10 +1098,11 @@ impl RawLua {
#[cfg(feature = "async")]
pub(crate) fn create_async_callback(&self, func: AsyncCallback) -> Result<Function> {
// Ensure that the coroutine library is loaded
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
unsafe {
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
load_from_std_lib(self.main_state, StdLib::COROUTINE)?;
load_std_libs(self.main_state(), StdLib::COROUTINE)?;
(*self.extra.get()).libs |= StdLib::COROUTINE;
}
}
@@ -1104,15 +1112,14 @@ impl RawLua {
// so the first upvalue is always valid
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
let extra = (*upvalue).extra.get();
callback_error_ext(state, extra, |nargs| {
callback_error_ext(state, extra, |extra, nargs| {
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
// The lock must be already held as the callback is executed
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
let args = MultiValue::from_stack_multi(nargs, rawlua)?;
let func = &*(*upvalue).data;
let fut = func(rawlua, args);
let fut = func(rawlua, nargs);
let extra = XRc::clone(&(*upvalue).extra);
let protect = !rawlua.unlikely_memory_error();
push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
@@ -1130,8 +1137,7 @@ impl RawLua {
unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
let extra = (*upvalue).extra.get();
callback_error_ext(state, extra, |_| {
callback_error_ext(state, (*upvalue).extra.get(), |extra, _| {
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
// The lock must be already held as the future is polled
let rawlua = (*extra).raw_lua();
@@ -1147,7 +1153,7 @@ impl RawLua {
}
Poll::Ready(nresults) => {
match nresults? {
nresults @ 0..=2 => {
nresults if nresults < 3 => {
// Fast path for up to 2 results without creating a table
ffi::lua_pushinteger(state, nresults as _);
if nresults > 0 {
@@ -1172,7 +1178,6 @@ impl RawLua {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let func = mem::transmute::<AsyncCallback, AsyncCallback<'static>>(func);
let extra = XRc::clone(&self.extra);
let protect = !self.unlikely_memory_error();
let upvalue = AsyncCallbackUpvalue { data: func, extra };
@@ -1198,15 +1203,13 @@ impl RawLua {
}
let lua = self.lua();
let coroutine = lua.globals().get::<_, Table>("coroutine")?;
let coroutine = lua.globals().get::<Table>("coroutine")?;
// Prepare environment for the async poller
let env = lua.create_table_with_capacity(0, 3)?;
env.set("get_poll", get_poll)?;
// Cache `yield` function
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
unsafe {
env.set("unpack", lua.create_c_function(unpack)?)?;
}
env.set("yield", coroutine.get::<Function>("yield")?)?;
env.set("unpack", unsafe { lua.create_c_function(unpack)? })?;
lua.load(
r#"
@@ -1248,7 +1251,7 @@ impl RawLua {
}
// Uses 3 stack spaces
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
#[inline(always)]
pub unsafe fn requiref(
state: *mut ffi::lua_State,
@@ -1344,6 +1347,12 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
ffi::lua_pop(state, 1);
}
#[cfg(feature = "luau")]
if libs.contains(StdLib::VECTOR) {
requiref(state, ffi::LUA_VECLIBNAME, ffi::luaopen_vector, 1)?;
ffi::lua_pop(state, 1);
}
if libs.contains(StdLib::MATH) {
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
ffi::lua_pop(state, 1);
@@ -1366,16 +1375,15 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
}
#[cfg(feature = "luajit")]
{
if libs.contains(StdLib::JIT) {
requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
ffi::lua_pop(state, 1);
}
if libs.contains(StdLib::JIT) {
requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
ffi::lua_pop(state, 1);
}
if libs.contains(StdLib::FFI) {
requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
ffi::lua_pop(state, 1);
}
#[cfg(feature = "luajit")]
if libs.contains(StdLib::FFI) {
requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
ffi::lua_pop(state, 1);
}
Ok(())
+3 -4
View File
@@ -8,7 +8,6 @@ use crate::state::{ExtraData, RawLua};
use crate::util::{self, get_internal_metatable, WrappedFailure};
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
// const MULTIVALUE_POOL_SIZE: usize = 64;
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
@@ -19,7 +18,7 @@ impl<'a> StateGuard<'a> {
}
}
impl<'a> Drop for StateGuard<'a> {
impl Drop for StateGuard<'_> {
fn drop(&mut self) {
self.0.state.set(self.1);
}
@@ -33,7 +32,7 @@ pub(super) unsafe fn callback_error_ext<F, R>(
f: F,
) -> R
where
F: FnOnce(c_int) -> Result<R>,
F: FnOnce(*mut ExtraData, c_int) -> Result<R>,
{
if extra.is_null() {
extra = ExtraData::get(state);
@@ -114,7 +113,7 @@ where
// to store a wrapped failure (error or panic) *before* we proceed.
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
match catch_unwind(AssertUnwindSafe(|| f(extra, nargs))) {
Ok(Ok(r)) => {
// Return unused `WrappedFailure` to the pool
prealloc_failure.release(state, extra);
+7 -2
View File
@@ -43,17 +43,22 @@ impl StdLib {
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
pub const PACKAGE: StdLib = StdLib(1 << 8);
/// [`buffer`](https://luau-lang.org/library#buffer-library) library
/// [`buffer`](https://luau.org/library#buffer-library) library
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub const BUFFER: StdLib = StdLib(1 << 9);
/// [`vector`](https://luau.org/library#vector-library) library
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub const VECTOR: StdLib = StdLib(1 << 10);
/// [`jit`](http://luajit.org/ext_jit.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const JIT: StdLib = StdLib(1 << 9);
pub const JIT: StdLib = StdLib(1 << 11);
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
///
+89 -40
View File
@@ -1,20 +1,20 @@
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::os::raw::c_void;
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
use std::{cmp, fmt, slice, str};
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::types::{LuaType, ValueRef};
#[cfg(feature = "serialize")]
use {
serde::ser::{Serialize, Serializer},
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::state::LuaGuard;
use crate::types::ValueRef;
/// Handle to an internal Lua string.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
@@ -45,7 +45,7 @@ impl String {
let BorrowedBytes(bytes, guard) = self.as_bytes();
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str",
to: "&str".to_string(),
message: Some(e.to_string()),
})?;
Ok(BorrowedStr(s, guard))
@@ -55,7 +55,11 @@ impl String {
///
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
/// bound to a weak Lua object.
///
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
/// [`Cow<'_, str>`]: std::borrow::Cow
///
/// # Examples
///
@@ -74,6 +78,16 @@ impl String {
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
}
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
/// contain non-Unicode data.
///
/// This may perform lossy conversion.
///
/// [`Display`]: fmt::Display
pub fn display(&self) -> impl fmt::Display + '_ {
Display(self)
}
/// Get the bytes that make up this string.
///
/// The returned slice will not contain the terminating nul byte, but will contain any nul
@@ -103,22 +117,24 @@ impl String {
BorrowedBytes(bytes, guard)
}
unsafe fn to_slice(&self) -> (&[u8], LuaGuard) {
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
unsafe fn to_slice(&self) -> (&[u8], Lua) {
let lua = self.0.lua.upgrade();
let slice = unsafe {
let rawlua = lua.lock();
let ref_thread = rawlua.ref_thread();
mlua_debug_assert!(
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
"string ref is not string type"
);
let mut size = 0;
// This will not trigger a 'm' error, because the reference is guaranteed to be of
// string type
let mut size = 0;
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
(slice::from_raw_parts(data as *const u8, size + 1), lua)
}
slice::from_raw_parts(data as *const u8, size + 1)
};
(slice, lua)
}
/// Converts this string to a generic C pointer.
@@ -141,27 +157,12 @@ impl fmt::Debug for String {
}
// Format as bytes
write!(f, "b\"")?;
for &b in bytes {
// https://doc.rust-lang.org/reference/tokens.html#byte-escapes
match b {
b'\n' => write!(f, "\\n")?,
b'\r' => write!(f, "\\r")?,
b'\t' => write!(f, "\\t")?,
b'\\' | b'"' => write!(f, "\\{}", b as char)?,
b'\0' => write!(f, "\\0")?,
// ASCII printable
0x20..=0x7e => write!(f, "{}", b as char)?,
_ => write!(f, "\\x{b:02x}")?,
}
}
write!(f, "\"")?;
Ok(())
write!(f, "b")?;
<bstr::BStr as fmt::Debug>::fmt(bstr::BStr::new(&bytes), f)
}
}
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
//
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
//
@@ -177,19 +178,34 @@ where
}
}
impl PartialEq<String> for String {
impl PartialEq for String {
fn eq(&self, other: &String) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialEq<&String> for String {
fn eq(&self, other: &&String) -> bool {
self.as_bytes() == other.as_bytes()
impl Eq for String {}
impl<T> PartialOrd<T> for String
where
T: AsRef<[u8]> + ?Sized,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(&other.as_ref())
}
}
impl Eq for String {}
impl PartialOrd for String {
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for String {
fn cmp(&self, other: &String) -> cmp::Ordering {
self.as_bytes().cmp(&other.as_bytes())
}
}
impl Hash for String {
fn hash<H: Hasher>(&self, state: &mut H) {
@@ -210,8 +226,17 @@ impl Serialize for String {
}
}
struct Display<'a>(&'a String);
impl fmt::Display for Display<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.0.as_bytes();
<bstr::BStr as fmt::Display>::fmt(bstr::BStr::new(&bytes), f)
}
}
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] LuaGuard);
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
impl Deref for BorrowedStr<'_> {
type Target = str;
@@ -257,6 +282,8 @@ where
}
}
impl Eq for BorrowedStr<'_> {}
impl<T> PartialOrd<T> for BorrowedStr<'_>
where
T: AsRef<str>,
@@ -266,8 +293,14 @@ where
}
}
impl Ord for BorrowedStr<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(other.0)
}
}
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] LuaGuard);
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
impl Deref for BorrowedBytes<'_> {
type Target = [u8];
@@ -307,6 +340,8 @@ where
}
}
impl Eq for BorrowedBytes<'_> {}
impl<T> PartialOrd<T> for BorrowedBytes<'_>
where
T: AsRef<[u8]>,
@@ -316,15 +351,25 @@ where
}
}
impl Ord for BorrowedBytes<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(other.0)
}
}
impl<'a> IntoIterator for BorrowedBytes<'a> {
type Item = &'a u8;
type IntoIter = slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
self.0.iter()
}
}
impl LuaType for String {
const TYPE_ID: c_int = ffi::LUA_TSTRING;
}
#[cfg(test)]
mod assertions {
use super::*;
@@ -333,4 +378,8 @@ mod assertions {
static_assertions::assert_not_impl_any!(String: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(String: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(BorrowedStr: Send, Sync);
}
+174 -211
View File
@@ -1,7 +1,19 @@
use std::collections::HashSet;
use std::fmt;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{LuaGuard, RawLua};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
use crate::types::{Integer, LuaType, ValueRef};
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
use crate::value::{Nil, Value};
#[cfg(feature = "async")]
use futures_util::future::{self, Either, Future};
#[cfg(feature = "serialize")]
use {
@@ -10,19 +22,8 @@ use {
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::private::Sealed;
use crate::state::{LuaGuard, RawLua};
use crate::types::{Integer, ValueRef};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
#[cfg(feature = "async")]
use std::future::Future;
/// Handle to an internal Lua table.
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct Table(pub(crate) ValueRef);
impl Table {
@@ -58,13 +59,17 @@ impl Table {
/// # }
/// ```
///
/// [`raw_set`]: #method.raw_set
pub fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
// Fast track
/// [`raw_set`]: Table::raw_set
pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
// Fast track (skip protected call)
if !self.has_metatable() {
return self.raw_set(key, value);
}
self.set_protected(key, value)
}
pub(crate) fn set_protected(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -101,13 +106,17 @@ impl Table {
/// # }
/// ```
///
/// [`raw_get`]: #method.raw_get
pub fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
// Fast track
/// [`raw_get`]: Table::raw_get
pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
// Fast track (skip protected call)
if !self.has_metatable() {
return self.raw_get(key);
}
self.get_protected(key)
}
pub(crate) fn get_protected<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -125,15 +134,15 @@ impl Table {
/// Checks whether the table contains a non-nil value for `key`.
///
/// This might invoke the `__index` metamethod.
pub fn contains_key<K: IntoLua>(&self, key: K) -> Result<bool> {
Ok(self.get::<_, Value>(key)? != Value::Nil)
pub fn contains_key(&self, key: impl IntoLua) -> Result<bool> {
Ok(self.get::<Value>(key)? != Value::Nil)
}
/// Appends a value to the back of the table.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn push<V: IntoLua>(&self, value: V) -> Result<()> {
// Fast track
pub fn push(&self, value: impl IntoLua) -> Result<()> {
// Fast track (skip protected call)
if !self.has_metatable() {
return self.raw_push(value);
}
@@ -158,7 +167,7 @@ impl Table {
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn pop<V: FromLua>(&self) -> Result<V> {
// Fast track
// Fast track (skip protected call)
if !self.has_metatable() {
return self.raw_pop();
}
@@ -209,23 +218,22 @@ impl Table {
/// # Ok(())
/// # }
/// ```
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
pub fn equals(&self, other: &Self) -> Result<bool> {
if self == other {
return Ok(true);
}
// Compare using __eq metamethod if exists
// 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 let Some(mt) = self.metatable() {
if mt.contains_key("__eq")? {
return mt.get::<_, Function>("__eq")?.call((self, other));
return mt.get::<Function>("__eq")?.call((self, other));
}
}
if let Some(mt) = other.get_metatable() {
if let Some(mt) = other.metatable() {
if mt.contains_key("__eq")? {
return mt.get::<_, Function>("__eq")?.call((self, other));
return mt.get::<Function>("__eq")?.call((self, other));
}
}
@@ -233,13 +241,13 @@ impl Table {
}
/// Sets a key-value pair without invoking metamethods.
pub fn raw_set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
#[cfg(feature = "luau")]
self.check_readonly_write(&lua)?;
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
@@ -258,7 +266,7 @@ impl Table {
}
/// Gets the value associated to `key` without invoking metamethods.
pub fn raw_get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -274,8 +282,10 @@ impl Table {
}
/// 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: IntoLua>(&self, idx: Integer, value: V) -> Result<()> {
/// `table[idx]`.
///
/// The worst case complexity is O(n), where n is the table length.
pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
let size = self.raw_len() as Integer;
if idx < 1 || idx > size + 1 {
return Err(Error::runtime("index out of bounds"));
@@ -301,13 +311,13 @@ impl Table {
}
/// Appends a value to the back of the table without invoking metamethods.
pub fn raw_push<V: IntoLua>(&self, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
#[cfg(feature = "luau")]
self.check_readonly_write(&lua)?;
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
@@ -330,12 +340,12 @@ impl Table {
/// Removes the last element from the table and returns it, without invoking metamethods.
pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
#[cfg(feature = "luau")]
self.check_readonly_write(&lua)?;
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
@@ -353,11 +363,11 @@ impl Table {
/// 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.
/// 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: IntoLua>(&self, key: K) -> Result<()> {
pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
let key = key.into_lua(lua.lua())?;
@@ -391,13 +401,13 @@ impl Table {
///
/// This method is useful to clear the table while keeping its capacity.
pub fn clear(&self) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua.lock();
unsafe {
#[cfg(feature = "luau")]
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
{
self.check_readonly_write(&lua)?;
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
}
#[cfg(not(feature = "luau"))]
{
@@ -429,11 +439,10 @@ impl Table {
/// Returns the result of the Lua `#` operator.
///
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
///
/// [`raw_len`]: #method.raw_len
/// This might invoke the `__len` metamethod. Use the [`Table::raw_len`] method if that is not
/// desired.
pub fn len(&self) -> Result<Integer> {
// Fast track
// Fast track (skip protected call)
if !self.has_metatable() {
return Ok(self.raw_len() as Integer);
}
@@ -483,8 +492,10 @@ impl Table {
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
///
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
pub fn get_metatable(&self) -> Option<Table> {
/// Unlike the [`getmetatable`] Lua function, this method ignores the `__metatable` field.
///
/// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
pub fn metatable(&self) -> Option<Table> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -500,6 +511,13 @@ impl Table {
}
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "please use `metatable` instead")]
#[cfg(not(tarpaulin_include))]
pub fn get_metatable(&self) -> Option<Table> {
self.metatable()
}
/// Sets or removes the metatable of this table.
///
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
@@ -532,14 +550,7 @@ impl Table {
#[inline]
pub fn has_metatable(&self) -> bool {
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
ffi::lua_pop(ref_thread, 1);
return true;
}
}
false
unsafe { !get_metatable_ptr(lua.ref_thread(), self.0.index).is_null() }
}
/// Sets `readonly` attribute on the table.
@@ -606,7 +617,6 @@ impl Table {
/// # }
/// ```
///
/// [`Result`]: crate::Result
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
TablePairs {
@@ -673,10 +683,6 @@ impl Table {
/// # Ok(())
/// # }
/// ```
///
/// [`pairs`]: #method.pairs
/// [`Result`]: crate::Result
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
TableSequence {
guard: self.0.lua.lock(),
@@ -686,8 +692,9 @@ impl Table {
}
}
#[cfg(feature = "serialize")]
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
/// Iterates over the sequence part of the table, invoking the given closure on each value.
#[doc(hidden)]
pub fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
where
V: FromLua,
{
@@ -710,13 +717,13 @@ impl Table {
/// Sets element value at position `idx` without invoking metamethods.
#[doc(hidden)]
pub fn raw_seti<V: IntoLua>(&self, idx: usize, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
#[cfg(feature = "luau")]
self.check_readonly_write(&lua)?;
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
@@ -752,8 +759,8 @@ impl Table {
#[cfg(feature = "luau")]
#[inline(always)]
pub(crate) fn check_readonly_write(&self) -> Result<()> {
if self.is_readonly() {
fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
if unsafe { ffi::lua_getreadonly(lua.ref_thread(), self.0.index) != 0 } {
return Err(Error::runtime("attempt to modify a readonly table"));
}
Ok(())
@@ -770,17 +777,41 @@ impl Table {
// Collect key/value pairs into a vector so we can sort them
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
// Sort keys
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
let is_sequence = (pairs.iter().enumerate())
.all(|(i, (k, _))| matches!(k, Value::Integer(n) if *n == (i + 1) as Integer));
if pairs.is_empty() {
return write!(fmt, "{{}}");
}
writeln!(fmt, "{{")?;
for (key, value) in pairs {
write!(fmt, "{}[", " ".repeat(ident + 2))?;
key.fmt_pretty(fmt, false, ident + 2, visited)?;
write!(fmt, "] = ")?;
value.fmt_pretty(fmt, true, ident + 2, visited)?;
writeln!(fmt, ",")?;
if is_sequence {
// Format as list
for (_, value) in pairs {
write!(fmt, "{}", " ".repeat(ident + 2))?;
value.fmt_pretty(fmt, true, ident + 2, visited)?;
writeln!(fmt, ",")?;
}
} else {
fn is_simple_key(key: &[u8]) -> bool {
key.iter().take(1).all(|c| c.is_ascii_alphabetic() || *c == b'_')
&& key.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'_')
}
for (key, value) in pairs {
match key {
Value::String(key) if is_simple_key(&key.as_bytes()) => {
write!(fmt, "{}{}", " ".repeat(ident + 2), key.display())?;
write!(fmt, " = ")?;
}
_ => {
write!(fmt, "{}[", " ".repeat(ident + 2))?;
key.fmt_pretty(fmt, false, ident + 2, visited)?;
write!(fmt, "] = ")?;
}
}
value.fmt_pretty(fmt, true, ident + 2, visited)?;
writeln!(fmt, ",")?;
}
}
write!(fmt, "{}}}", " ".repeat(ident))
}
@@ -791,20 +822,7 @@ impl fmt::Debug for Table {
if fmt.alternate() {
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
}
fmt.write_fmt(format_args!("Table({:?})", self.0))
}
}
impl PartialEq for Table {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl AsRef<Table> for Table {
#[inline]
fn as_ref(&self) -> &Self {
self
fmt.debug_tuple("Table").field(&self.0).finish()
}
}
@@ -858,141 +876,86 @@ where
}
}
/// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt: Sealed {
/// 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: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti;
impl LuaType for Table {
const TYPE_ID: c_int = ffi::LUA_TTABLE;
}
impl TableExt for Table {
fn call<A, R>(&self, args: A) -> Result<R>
impl ObjectLike for Table {
#[inline]
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
self.get(key)
}
#[inline]
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
self.set(key, value)
}
#[inline]
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
// Convert table to a function and call via pcall that respects the `__call` metamethod.
Function(self.0.clone()).call(args)
Function(self.0.copy()).call(args)
}
#[cfg(feature = "async")]
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
#[inline]
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
async move {
let func = Function(self.0.clone());
func.call_async(args?).await
}
Function(self.0.copy()).call_async(args)
}
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
#[inline]
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.get::<_, Function>(name)?.call((self, args))
}
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.get::<_, Function>(name)?.call(args)
self.call_function(name, (self, args))
}
#[cfg(feature = "async")]
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.call_async_function(name, (self, args))
}
#[inline]
fn call_function<R: FromLuaMulti>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R> {
match self.get(name)? {
Value::Function(func) => func.call(args),
val => {
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
Err(Error::runtime(msg))
}
}
}
#[cfg(feature = "async")]
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
#[inline]
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
async move {
let func = self.get::<_, Function>(name)?;
func.call_async(args?).await
match self.get(name) {
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
Ok(val) => {
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
}
Err(err) => Either::Right(future::ready(Err(err))),
}
}
#[inline]
fn to_string(&self) -> Result<StdString> {
Value::Table(Table(self.0.copy())).to_string()
}
}
/// A wrapped [`Table`] with customized serialization behavior.
@@ -1028,7 +991,7 @@ impl<'a> SerializableTable<'a> {
}
#[cfg(feature = "serialize")]
impl<'a> Serialize for SerializableTable<'a> {
impl Serialize for SerializableTable<'_> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
@@ -1062,7 +1025,7 @@ impl<'a> Serialize for SerializableTable<'a> {
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
.map_err(|err| {
serialize_err = Some(err);
Error::SerializeError(String::new())
Error::SerializeError(StdString::new())
})
});
convert_result(res, serialize_err)?;
@@ -1087,7 +1050,7 @@ impl<'a> Serialize for SerializableTable<'a> {
)
.map_err(|err| {
serialize_err = Some(err);
Error::SerializeError(String::new())
Error::SerializeError(StdString::new())
})
};
@@ -1119,7 +1082,7 @@ pub struct TablePairs<'a, K, V> {
_phantom: PhantomData<(K, V)>,
}
impl<'a, K, V> Iterator for TablePairs<'a, K, V>
impl<K, V> Iterator for TablePairs<'_, K, V>
where
K: FromLua,
V: FromLua,
@@ -1142,11 +1105,11 @@ where
// a permitted operation.
// It fails only if the key is not found (never existed) which seems impossible scenario.
if ffi::lua_next(state, -2) != 0 {
let key = lua.stack_value(-2);
let key = lua.stack_value(-2, None);
Ok(Some((
key.clone(),
K::from_lua(key, lua.lua())?,
V::from_stack(-1, &lua)?,
V::from_stack(-1, lua)?,
)))
} else {
Ok(None)
@@ -1179,7 +1142,7 @@ pub struct TableSequence<'a, V> {
_phantom: PhantomData<V>,
}
impl<'a, V> Iterator for TableSequence<'a, V>
impl<V> Iterator for TableSequence<'_, V>
where
V: FromLua,
{
@@ -1199,7 +1162,7 @@ where
ffi::LUA_TNIL => None,
_ => {
self.index += 1;
Some(V::from_stack(-1, &lua))
Some(V::from_stack(-1, lua))
}
}
}
+107 -91
View File
@@ -1,12 +1,13 @@
use std::fmt;
use std::os::raw::{c_int, c_void};
use crate::error::{Error, Result};
#[allow(unused)]
use crate::state::Lua;
use crate::state::RawLua;
use crate::types::ValueRef;
use crate::traits::{FromLuaMulti, IntoLuaMulti};
use crate::types::{LuaType, ValueRef};
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
#[cfg(not(feature = "luau"))]
use crate::{
@@ -16,7 +17,6 @@ use crate::{
#[cfg(feature = "async")]
use {
crate::value::MultiValue,
futures_util::stream::Stream,
std::{
future::Future,
@@ -30,20 +30,20 @@ use {
/// Status of a Lua thread (coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadStatus {
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
/// The thread was just created or is suspended (yielded).
///
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
///
/// [`Thread::resume`]: crate::Thread::resume
Resumable,
/// Either the thread has finished executing, or the thread is currently running.
Unresumable,
/// The thread is currently running.
Running,
/// The thread has finished executing.
Finished,
/// The thread has raised a Lua error during execution.
Error,
}
/// Handle to an internal Lua thread (coroutine).
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
#[cfg(feature = "send")]
@@ -60,32 +60,32 @@ unsafe impl Sync for Thread {}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AsyncThread<R> {
pub struct AsyncThread<A, R> {
thread: Thread,
init_args: Option<Result<MultiValue>>,
init_args: Option<A>,
ret: PhantomData<R>,
recycle: bool,
}
impl Thread {
#[inline(always)]
const fn state(&self) -> *mut ffi::lua_State {
fn state(&self) -> *mut ffi::lua_State {
self.1
}
/// Resumes execution of this thread.
///
/// Equivalent to `coroutine.resume`.
/// Equivalent to [`coroutine.resume`].
///
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
/// are passed to its main function.
/// Passes `args` as arguments to the thread. If the coroutine has called [`coroutine.yield`],
/// it will return these arguments. Otherwise, the coroutine wasn't yet started, so the
/// arguments are passed to its main function.
///
/// If the thread is no longer in `Active` state (meaning it has finished execution or
/// encountered an error), this will return `Err(CoroutineInactive)`, otherwise will return `Ok`
/// as follows:
/// If the thread is no longer resumable (meaning it has finished execution or encountered an
/// error), this will return [`Error::CoroutineUnresumable`], otherwise will return `Ok` as
/// follows:
///
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
/// If the thread calls [`coroutine.yield`], returns the values passed to `yield`. If the thread
/// `return`s values from its main function, returns those.
///
/// # Examples
@@ -103,26 +103,27 @@ impl Thread {
/// end)
/// "#).eval()?;
///
/// assert_eq!(thread.resume::<_, u32>(42)?, 123);
/// assert_eq!(thread.resume::<_, u32>(43)?, 987);
/// assert_eq!(thread.resume::<u32>(42)?, 123);
/// assert_eq!(thread.resume::<u32>(43)?, 987);
///
/// // The coroutine has now returned, so `resume` will fail
/// match thread.resume::<_, u32>(()) {
/// Err(Error::CoroutineInactive) => {},
/// match thread.resume::<u32>(()) {
/// Err(Error::CoroutineUnresumable) => {},
/// unexpected => panic!("unexpected result {:?}", unexpected),
/// }
/// # Ok(())
/// # }
/// ```
pub fn resume<A, R>(&self, args: A) -> Result<R>
///
/// [`coroutine.resume`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.resume
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
if unsafe { self.status_unprotected() } != ThreadStatus::Resumable {
return Err(Error::CoroutineInactive);
if self.status_inner(&lua) != ThreadStatus::Resumable {
return Err(Error::CoroutineUnresumable);
}
let state = lua.state();
@@ -131,7 +132,7 @@ impl Thread {
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let nresults = self.resume_inner(args)?;
let nresults = self.resume_inner(&lua, args)?;
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
@@ -142,12 +143,11 @@ impl Thread {
/// Resumes execution of this thread.
///
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
unsafe fn resume_inner<A: IntoLuaMulti>(&self, args: A) -> Result<c_int> {
let lua = self.0.lua.lock();
unsafe fn resume_inner(&self, lua: &RawLua, args: impl IntoLuaMulti) -> Result<c_int> {
let state = lua.state();
let thread_state = self.state();
let nargs = args.push_into_stack_multi(&lua)?;
let nargs = args.push_into_stack_multi(lua)?;
if nargs > 0 {
check_stack(thread_state, nargs)?;
ffi::lua_xmove(state, thread_state, nargs);
@@ -170,37 +170,35 @@ impl Thread {
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
let _guard = self.0.lua.lock();
unsafe { self.status_unprotected() }
self.status_inner(&self.0.lua.lock())
}
/// Gets the status of the thread without locking the Lua state.
pub(crate) unsafe fn status_unprotected(&self) -> ThreadStatus {
/// Gets the status of the thread (internal implementation).
pub(crate) fn status_inner(&self, lua: &RawLua) -> ThreadStatus {
let thread_state = self.state();
// FIXME: skip double lock
if thread_state == self.0.lua.lock().state() {
// The coroutine is currently running
return ThreadStatus::Unresumable;
if thread_state == lua.state() {
// The thread is currently running
return ThreadStatus::Running;
}
let status = ffi::lua_status(thread_state);
let status = unsafe { ffi::lua_status(thread_state) };
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
ThreadStatus::Error
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
} else if status == ffi::LUA_YIELD || unsafe { ffi::lua_gettop(thread_state) > 0 } {
ThreadStatus::Resumable
} else {
ThreadStatus::Unresumable
ThreadStatus::Finished
}
}
/// Sets a 'hook' function that will periodically be called as Lua code executes.
/// Sets a hook function that will periodically be called as Lua code executes.
///
/// This function is similar or [`Lua::set_hook()`] except that it sets for the thread.
/// To remove a hook call [`Lua::remove_hook()`].
/// This function is similar or [`Lua::set_hook`] except that it sets for the thread.
/// To remove a hook call [`Lua::remove_hook`].
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
F: Fn(&Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
{
let lua = self.0.lua.lock();
unsafe {
@@ -226,10 +224,11 @@ impl Thread {
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
pub fn reset(&self, func: crate::function::Function) -> Result<()> {
let lua = self.0.lua.lock();
let thread_state = self.state();
if thread_state == lua.state() {
if self.status_inner(&lua) == ThreadStatus::Running {
return Err(Error::runtime("cannot reset a running thread"));
}
let thread_state = self.state();
unsafe {
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
let status = ffi::lua_resetthread(thread_state);
@@ -256,21 +255,22 @@ impl Thread {
}
}
/// Converts Thread to an AsyncThread which implements [`Future`] and [`Stream`] traits.
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
///
/// `args` are passed as arguments to the thread function for first call.
/// The object calls [`resume()`] while polling and also allows to run rust futures
/// The object calls [`resume`] while polling and also allow to run Rust futures
/// to completion using an executor.
///
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
/// values whereas Future version discards that values and poll until the final
/// Using [`AsyncThread`] as a [`Stream`] allow to iterate through [`coroutine.yield`]
/// values whereas [`Future`] version discards that values and poll until the final
/// one (returned from the thread function).
///
/// Requires `feature = "async"`
///
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
/// [`resume`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
///
/// # Examples
///
@@ -290,7 +290,7 @@ impl Thread {
/// end)
/// "#).eval()?;
///
/// let mut stream = thread.into_async::<_, i64>(1);
/// let mut stream = thread.into_async::<i64>(1);
/// let mut sum = 0;
/// while let Some(n) = stream.try_next().await? {
/// sum += n;
@@ -303,13 +303,10 @@ impl Thread {
/// ```
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<R>
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> AsyncThread<impl IntoLuaMulti, R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
AsyncThread {
thread: self,
init_args: Some(args),
@@ -323,7 +320,7 @@ impl 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()`].
/// 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.
@@ -332,24 +329,28 @@ impl Thread {
///
/// ```
/// # use mlua::{Lua, Result};
/// # #[cfg(feature = "luau")]
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
/// lua2.load("var = 123").exec()?;
/// assert_eq!(lua2.globals().get::<_, u32>("var")?, 123);
/// 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);
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
/// # Ok(())
/// # }
///
/// # #[cfg(not(feature = "luau"))]
/// # fn main() { }
/// ```
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", docsrs))]
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
@@ -374,14 +375,24 @@ impl Thread {
}
}
impl fmt::Debug for Thread {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Thread").field(&self.0).finish()
}
}
impl PartialEq for Thread {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl LuaType for Thread {
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
}
#[cfg(feature = "async")]
impl<R> AsyncThread<R> {
impl<A, R> AsyncThread<A, R> {
#[inline]
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
self.recycle = recyclable;
@@ -390,19 +401,20 @@ impl<R> AsyncThread<R> {
#[cfg(feature = "async")]
#[cfg(any(feature = "lua54", feature = "luau"))]
impl<R> Drop for AsyncThread<R> {
impl<A, R> Drop for AsyncThread<A, R> {
fn drop(&mut self) {
if self.recycle {
unsafe {
let lua = self.thread.0.lua.lock();
// For Lua 5.4 this also closes all pending to-be-closed variables
if !lua.recycle_thread(&mut self.thread) {
#[cfg(feature = "lua54")]
if self.thread.status_unprotected() == ThreadStatus::Error {
#[cfg(not(feature = "vendored"))]
ffi::lua_resetthread(self.thread.state());
#[cfg(feature = "vendored")]
ffi::lua_closethread(self.thread.state(), lua.state());
if let Some(lua) = self.thread.0.lua.try_lock() {
unsafe {
// For Lua 5.4 this also closes all pending to-be-closed variables
if !lua.recycle_thread(&mut self.thread) {
#[cfg(feature = "lua54")]
if self.thread.status_inner(&lua) == ThreadStatus::Error {
#[cfg(not(feature = "vendored"))]
ffi::lua_resetthread(self.thread.state());
#[cfg(feature = "vendored")]
ffi::lua_closethread(self.thread.state(), lua.state());
}
}
}
}
@@ -411,18 +423,18 @@ impl<R> Drop for AsyncThread<R> {
}
#[cfg(feature = "async")]
impl<R: FromLuaMulti> Stream for AsyncThread<R> {
impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
type Item = Result<R>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let lua = self.thread.0.lua.lock();
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
return Poll::Ready(None);
}
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
if self.thread.status_unprotected() != ThreadStatus::Resumable {
return Poll::Ready(None);
}
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(&lua, cx.waker());
@@ -430,9 +442,9 @@ impl<R: FromLuaMulti> Stream for AsyncThread<R> {
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
let nresults = if let Some(args) = this.init_args.take() {
this.thread.resume_inner(args?)?
this.thread.resume_inner(&lua, args)?
} else {
this.thread.resume_inner(())?
this.thread.resume_inner(&lua, ())?
};
if nresults == 1 && is_poll_pending(thread_state) {
@@ -449,18 +461,18 @@ impl<R: FromLuaMulti> Stream for AsyncThread<R> {
}
#[cfg(feature = "async")]
impl<R: FromLuaMulti> Future for AsyncThread<R> {
impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let lua = self.thread.0.lua.lock();
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
return Poll::Ready(Err(Error::CoroutineUnresumable));
}
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
if self.thread.status_unprotected() != ThreadStatus::Resumable {
return Poll::Ready(Err(Error::CoroutineInactive));
}
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(&lua, cx.waker());
@@ -468,9 +480,9 @@ impl<R: FromLuaMulti> Future for AsyncThread<R> {
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
let nresults = if let Some(args) = this.init_args.take() {
this.thread.resume_inner(args?)?
this.thread.resume_inner(&lua, args)?
} else {
this.thread.resume_inner(())?
this.thread.resume_inner(&lua, ())?
};
if nresults == 1 && is_poll_pending(thread_state) {
@@ -518,7 +530,7 @@ impl<'lua, 'a> WakerGuard<'lua, 'a> {
}
#[cfg(feature = "async")]
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
impl Drop for WakerGuard<'_, '_> {
fn drop(&mut self) {
unsafe { self.lua.set_waker(self.prev) };
}
@@ -532,4 +544,8 @@ mod assertions {
static_assertions::assert_not_impl_any!(Thread: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Thread: Send, Sync);
#[cfg(all(feature = "async", not(feature = "send")))]
static_assertions::assert_not_impl_any!(AsyncThread<(), ()>: Send);
#[cfg(all(feature = "async", feature = "send"))]
static_assertions::assert_impl_all!(AsyncThread<(), ()>: Send, Sync);
}
+311
View File
@@ -0,0 +1,311 @@
use std::os::raw::c_int;
use std::string::String as StdString;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::multi::MultiValue;
use crate::private::Sealed;
use crate::state::{Lua, RawLua};
use crate::types::MaybeSend;
use crate::util::{check_stack, short_type_name};
use crate::value::Value;
#[cfg(feature = "async")]
use std::future::Future;
/// Trait for types convertible to [`Value`].
pub trait IntoLua: Sized {
/// Performs the conversion.
fn into_lua(self, lua: &Lua) -> Result<Value>;
/// Pushes the value into the Lua stack.
///
/// # Safety
/// This method does not check Lua stack space.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_value(&self.into_lua(lua.lua())?)
}
}
/// Trait for types convertible from [`Value`].
pub trait FromLua: Sized {
/// Performs the conversion.
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
/// `i` is the argument index (position),
/// `to` is a function name that received the argument.
#[doc(hidden)]
#[inline]
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
/// Performs the conversion for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx, None), lua.lua())
}
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
}
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of [`IntoLua`], allowing any number of resulting Lua values instead of
/// just one. Any type that implements [`IntoLua`] will automatically implement this trait.
pub trait IntoLuaMulti: Sized {
/// Performs the conversion.
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
/// Pushes the values into the Lua stack.
///
/// Returns number of pushed values.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let values = self.into_lua_multi(lua.lua())?;
let len: c_int = values.len().try_into().unwrap();
unsafe {
check_stack(lua.state(), len + 1)?;
for val in &values {
lua.push_value(val)?;
}
}
Ok(len)
}
}
/// Trait for types that can be created from an arbitrary number of Lua values.
///
/// This is a generalization of [`FromLua`], allowing an arbitrary number of Lua values to
/// participate in the conversion. Any type that implements [`FromLua`] will automatically
/// implement this trait.
pub trait FromLuaMulti: Sized {
/// Performs the conversion.
///
/// In case `values` contains more values than needed to perform the conversion, the excess
/// values should be ignored. This reflects the semantics of Lua when calling a function or
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
/// Performs the conversion for a list of arguments.
///
/// `i` is an index (position) of the first argument,
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(args, lua)
}
/// Performs the conversion for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
let mut values = MultiValue::with_capacity(nvals as usize);
for idx in 0..nvals {
values.push_back(lua.stack_value(-nvals + idx, None));
}
Self::from_lua_multi(values, lua.lua())
}
/// Same as `from_lua_args` but for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
let _ = (i, to);
Self::from_stack_multi(nargs, lua)
}
}
/// A trait for types that can be used as Lua objects (usually table and userdata).
pub trait ObjectLike: Sealed {
/// Gets the value associated to `key` from the object, assuming it has `__index` metamethod.
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V>;
/// Sets the value associated to `key` in the object, assuming it has `__newindex` metamethod.
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()>;
/// Calls the object as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the object as its first argument, followed by the passed
/// arguments.
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti;
/// Asynchronously calls the object as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the object as its first argument, followed by the passed
/// arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
R: FromLuaMulti;
/// Gets the function associated to key `name` from the object and calls it,
/// passing the object itself along with `args` as function arguments.
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti;
/// Gets the function associated to key `name` from the object and asynchronously calls it,
/// passing the object itself along with `args` as function arguments.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
R: FromLuaMulti;
/// Gets the function associated to key `name` from the object and calls it,
/// passing `args` as function arguments.
///
/// This might invoke the `__index` metamethod.
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti;
/// Gets the function associated to key `name` from the object and asynchronously calls it,
/// passing `args` as function arguments.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
R: FromLuaMulti;
/// Converts the object to a string in a human-readable format.
///
/// This might invoke the `__tostring` metamethod.
fn to_string(&self) -> Result<StdString>;
}
/// A trait for types that can be used as Lua functions.
pub trait LuaNativeFn<A: FromLuaMulti> {
type Output: IntoLuaMulti;
fn call(&self, args: A) -> Self::Output;
}
/// A trait for types with mutable state that can be used as Lua functions.
pub trait LuaNativeFnMut<A: FromLuaMulti> {
type Output: IntoLuaMulti;
fn call(&mut self, args: A) -> Self::Output;
}
/// A trait for types that returns a future and can be used as Lua functions.
#[cfg(feature = "async")]
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
type Output: IntoLuaMulti;
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
}
macro_rules! impl_lua_native_fn {
($($A:ident),*) => {
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
where
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
($($A,)*): FromLuaMulti,
R: IntoLuaMulti,
{
type Output = R;
#[allow(non_snake_case)]
fn call(&self, args: ($($A,)*)) -> Self::Output {
let ($($A,)*) = args;
self($($A,)*)
}
}
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
where
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
($($A,)*): FromLuaMulti,
R: IntoLuaMulti,
{
type Output = R;
#[allow(non_snake_case)]
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
let ($($A,)*) = args;
self($($A,)*)
}
}
#[cfg(feature = "async")]
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
where
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
($($A,)*): FromLuaMulti,
Fut: Future<Output = R> + MaybeSend + 'static,
R: IntoLuaMulti,
{
type Output = R;
#[allow(non_snake_case)]
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
let ($($A,)*) = args;
self($($A,)*)
}
}
};
}
impl_lua_native_fn!();
impl_lua_native_fn!(A);
impl_lua_native_fn!(A, B);
impl_lua_native_fn!(A, B, C);
impl_lua_native_fn!(A, B, C, D);
impl_lua_native_fn!(A, B, C, D, E);
impl_lua_native_fn!(A, B, C, D, E, F);
impl_lua_native_fn!(A, B, C, D, E, F, G);
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
pub(crate) trait ShortTypeName {
#[inline(always)]
fn type_name() -> StdString {
short_type_name::<Self>()
}
}
impl<T> ShortTypeName for T {}
+47 -260
View File
@@ -1,42 +1,31 @@
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::os::raw::{c_int, c_void};
use std::rc::Rc;
use std::sync::Arc;
use std::{fmt, mem, ptr};
use parking_lot::Mutex;
use crate::error::Result;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::state::{ExtraData, Lua, RawLua, WeakLua};
#[cfg(feature = "async")]
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
#[cfg(all(feature = "luau", feature = "serialize"))]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
use crate::state::{ExtraData, Lua, RawLua};
// Re-export mutex wrappers
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
#[cfg(all(feature = "async", feature = "send"))]
pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;
#[cfg(all(feature = "async", not(feature = "send")))]
pub(crate) type BoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>;
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
pub use either::Either;
pub use registry_key::RegistryKey;
pub(crate) use value_ref::ValueRef;
/// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
pub type Number = ffi::lua_Number;
// Represents different subtypes wrapped to AnyUserData
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum SubtypeId {
None,
#[cfg(feature = "luau")]
Buffer,
#[cfg(feature = "luajit")]
CData,
}
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
@@ -46,38 +35,49 @@ unsafe impl Send for LightUserData {}
#[cfg(feature = "send")]
unsafe impl Sync for LightUserData {}
pub(crate) type Callback<'a> = Box<dyn Fn(&'a RawLua, c_int) -> Result<c_int> + 'static>;
#[cfg(feature = "send")]
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send + 'static>;
#[cfg(not(feature = "send"))]
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
}
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static>>;
pub(crate) type CallbackUpvalue = Upvalue<Option<Callback>>;
#[cfg(all(feature = "async", feature = "send"))]
pub(crate) type AsyncCallback =
Box<dyn for<'a> Fn(&'a RawLua, c_int) -> BoxFuture<'a, Result<c_int>> + Send + 'static>;
#[cfg(all(feature = "async", not(feature = "send")))]
pub(crate) type AsyncCallback =
Box<dyn for<'a> Fn(&'a RawLua, c_int) -> BoxFuture<'a, Result<c_int>> + 'static>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallback<'a> =
Box<dyn Fn(&'a RawLua, MultiValue) -> LocalBoxFuture<'a, Result<c_int>> + 'static>;
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static>>;
pub(crate) type AsyncPollUpvalue = Upvalue<BoxFuture<'static, Result<c_int>>>;
#[cfg(feature = "async")]
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<c_int>>>;
/// Type to set next Luau VM action after executing interrupt function.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
/// Type to set next Lua VM action after executing interrupt or hook function.
pub enum VmState {
Continue,
/// Yield the current thread.
///
/// Supported by Lua 5.3+ and Luau.
Yield,
}
#[cfg(all(feature = "send", not(feature = "luau")))]
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<VmState> + Send>;
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()>>;
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "luau"))]
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
@@ -91,6 +91,7 @@ pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send
#[cfg(all(not(feature = "send"), feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()>>;
/// A trait that adds `Send` requirement if `send` feature is enabled.
#[cfg(feature = "send")]
pub trait MaybeSend: Send {}
#[cfg(feature = "send")]
@@ -101,247 +102,33 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
/// A Luau vector type.
///
/// By default vectors are 3-dimensional, but can be 4-dimensional
/// if the `luau-vector4` feature is enabled.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
#[cfg(any(feature = "luau", doc))]
impl fmt::Display for Vector {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "luau-vector4"))]
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
#[cfg(feature = "luau-vector4")]
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
}
}
#[cfg(any(feature = "luau", doc))]
impl Vector {
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
/// Creates a new vector.
#[cfg(not(feature = "luau-vector4"))]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self([x, y, z])
}
/// Creates a new vector.
#[cfg(feature = "luau-vector4")]
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self([x, y, z, w])
}
/// Creates a new vector with all components set to `0.0`.
#[doc(hidden)]
pub const fn zero() -> Self {
Self([0.0; Self::SIZE])
}
/// Returns 1st component of the vector.
pub const fn x(&self) -> f32 {
self.0[0]
}
/// Returns 2nd component of the vector.
pub const fn y(&self) -> f32 {
self.0[1]
}
/// Returns 3rd component of the vector.
pub const fn z(&self) -> f32 {
self.0[2]
}
/// Returns 4th component of the vector.
#[cfg(any(feature = "luau-vector4", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
pub const fn w(&self) -> f32 {
self.0[3]
}
}
#[cfg(all(feature = "luau", feature = "serialize"))]
impl Serialize for Vector {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
ts.serialize_field(&self.x())?;
ts.serialize_field(&self.y())?;
ts.serialize_field(&self.z())?;
#[cfg(feature = "luau-vector4")]
ts.serialize_field(&self.w())?;
ts.end()
}
}
#[cfg(any(feature = "luau", doc))]
impl PartialEq<[f32; Self::SIZE]> for Vector {
#[inline]
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
self.0 == *other
}
}
pub(crate) struct DestructedUserdata;
/// An auto generated key into the Lua registry.
///
/// 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
/// 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
/// [`AnyUserData::set_user_value`] / [`AnyUserData::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::user_value`]: crate::AnyUserData::user_value
pub struct RegistryKey {
pub(crate) registry_id: i32,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
pub(crate) trait LuaType {
const TYPE_ID: c_int;
}
impl fmt::Debug for RegistryKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RegistryKey({})", self.id())
}
impl LuaType for bool {
const TYPE_ID: c_int = ffi::LUA_TBOOLEAN;
}
impl Hash for RegistryKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id().hash(state)
}
impl LuaType for Number {
const TYPE_ID: c_int = ffi::LUA_TNUMBER;
}
impl PartialEq for RegistryKey {
fn eq(&self, other: &RegistryKey) -> bool {
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
}
}
impl Eq for RegistryKey {}
impl Drop for RegistryKey {
fn drop(&mut self) {
let registry_id = self.id();
// We don't need to collect nil slot
if registry_id > ffi::LUA_REFNIL {
let mut unref_list = self.unref_list.lock();
if let Some(list) = unref_list.as_mut() {
list.push(registry_id);
}
}
}
}
impl RegistryKey {
/// Creates a new instance of `RegistryKey`
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
RegistryKey {
registry_id: id,
unref_list,
}
}
/// Returns the underlying Lua reference of this `RegistryKey`
#[inline(always)]
pub fn id(&self) -> c_int {
self.registry_id
}
/// Sets the unique Lua reference key of this `RegistryKey`
#[inline(always)]
pub(crate) fn set_id(&mut self, id: c_int) {
self.registry_id = id;
}
/// Destroys the `RegistryKey` without adding to the unref list
pub(crate) fn take(self) -> i32 {
let registry_id = self.id();
unsafe {
ptr::read(&self.unref_list);
mem::forget(self);
}
registry_id
}
}
pub(crate) struct ValueRef {
pub(crate) lua: WeakLua,
pub(crate) index: c_int,
pub(crate) drop: bool,
}
impl ValueRef {
#[inline]
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
ValueRef {
lua: lua.weak().clone(),
index,
drop: true,
}
}
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
let lua = self.lua.lock();
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
}
}
impl fmt::Debug for ValueRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:p})", self.to_pointer())
}
}
impl Clone for ValueRef {
fn clone(&self) -> Self {
unsafe { self.lua.lock().clone_ref(self) }
}
}
impl Drop for ValueRef {
fn drop(&mut self) {
if self.drop {
if let Some(lua) = self.lua.try_lock() {
unsafe { lua.drop_ref(self) };
}
}
}
}
impl PartialEq for ValueRef {
fn eq(&self, other: &Self) -> bool {
assert!(
self.lua == other.lua,
"Lua instance passed Value created from a different main Lua state"
);
let lua = self.lua.lock();
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
}
impl LuaType for LightUserData {
const TYPE_ID: c_int = ffi::LUA_TLIGHTUSERDATA;
}
mod app_data;
mod registry_key;
mod sync;
mod value_ref;
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(ValueRef: Send);
#[cfg(feature = "send")]
+54 -18
View File
@@ -1,5 +1,5 @@
use std::any::{Any, TypeId};
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult;
@@ -41,30 +41,66 @@ impl AppData {
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
}
#[inline]
#[track_caller]
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRef {
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
borrow: &self.borrow,
_guard: guard,
})
match self.try_borrow(guard) {
Ok(data) => data,
Err(err) => panic!("already mutably borrowed: {err:?}"),
}
}
pub(crate) fn try_borrow<T: 'static>(
&self,
guard: Option<LuaGuard>,
) -> Result<Option<AppDataRef<T>>, BorrowError> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())
.map(|c| c.try_borrow())
.transpose()?
.and_then(|data| Ref::filter_map(data, |data| data.downcast_ref()).ok());
match data {
Some(data) => {
self.borrow.set(self.borrow.get() + 1);
Ok(Some(AppDataRef {
data,
borrow: &self.borrow,
_guard: guard,
}))
}
None => Ok(None),
}
}
#[inline]
#[track_caller]
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
match self.try_borrow_mut(guard) {
Ok(data) => data,
Err(err) => panic!("already borrowed: {err:?}"),
}
}
pub(crate) fn try_borrow_mut<T: 'static>(
&self,
guard: Option<LuaGuard>,
) -> Result<Option<AppDataRefMut<T>>, BorrowMutError> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow_mut();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRefMut {
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
borrow: &self.borrow,
_guard: guard,
})
.get(&TypeId::of::<T>())
.map(|c| c.try_borrow_mut())
.transpose()?
.and_then(|data| RefMut::filter_map(data, |data| data.downcast_mut()).ok());
match data {
Some(data) => {
self.borrow.set(self.borrow.get() + 1);
Ok(Some(AppDataRefMut {
data,
borrow: &self.borrow,
_guard: guard,
}))
}
None => Ok(None),
}
}
#[track_caller]
+100
View File
@@ -0,0 +1,100 @@
use std::hash::{Hash, Hasher};
use std::os::raw::c_int;
use std::sync::Arc;
use std::{fmt, mem, ptr};
use parking_lot::Mutex;
/// An auto generated key into the Lua registry.
///
/// 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 *easy*
/// to accidentally cause reference cycles that the Lua garbage collector cannot resolve. Instead of
/// placing a [`RegistryKey`] into a [`UserData`] type, consider to use
/// [`AnyUserData::set_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
pub struct RegistryKey {
pub(crate) registry_id: i32,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
}
impl fmt::Debug for RegistryKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RegistryKey({})", self.id())
}
}
impl Hash for RegistryKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id().hash(state)
}
}
impl PartialEq for RegistryKey {
fn eq(&self, other: &RegistryKey) -> bool {
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
}
}
impl Eq for RegistryKey {}
impl Drop for RegistryKey {
fn drop(&mut self) {
let registry_id = self.id();
// We don't need to collect nil slot
if registry_id > ffi::LUA_REFNIL {
let mut unref_list = self.unref_list.lock();
if let Some(list) = unref_list.as_mut() {
list.push(registry_id);
}
}
}
}
impl RegistryKey {
/// Creates a new instance of `RegistryKey`
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
RegistryKey {
registry_id: id,
unref_list,
}
}
/// Returns the underlying Lua reference of this `RegistryKey`
#[inline(always)]
pub fn id(&self) -> c_int {
self.registry_id
}
/// Sets the unique Lua reference key of this `RegistryKey`
#[inline(always)]
pub(crate) fn set_id(&mut self, id: c_int) {
self.registry_id = id;
}
/// Destroys the `RegistryKey` without adding to the unref list
pub(crate) fn take(self) -> i32 {
let registry_id = self.id();
unsafe {
ptr::read(&self.unref_list);
mem::forget(self);
}
registry_id
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
}
+1 -1
View File
@@ -53,7 +53,7 @@ mod inner {
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
impl<'a, T> Deref for ReentrantMutexGuard<'a, T> {
impl<T> Deref for ReentrantMutexGuard<'_, T> {
type Target = T;
#[inline(always)]
+71
View File
@@ -0,0 +1,71 @@
use std::fmt;
use std::os::raw::{c_int, c_void};
use crate::state::{RawLua, WeakLua};
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
pub(crate) struct ValueRef {
pub(crate) lua: WeakLua,
pub(crate) index: c_int,
pub(crate) drop: bool,
}
impl ValueRef {
#[inline]
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
ValueRef {
lua: lua.weak().clone(),
index,
drop: true,
}
}
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
let lua = self.lua.lock();
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
}
/// Returns a copy of the value, which is valid as long as the original value is held.
#[inline]
pub(crate) fn copy(&self) -> Self {
ValueRef {
lua: self.lua.clone(),
index: self.index,
drop: false,
}
}
}
impl fmt::Debug for ValueRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:p})", self.to_pointer())
}
}
impl Clone for ValueRef {
fn clone(&self) -> Self {
unsafe { self.lua.lock().clone_ref(self) }
}
}
impl Drop for ValueRef {
fn drop(&mut self) {
if self.drop {
if let Some(lua) = self.lua.try_lock() {
unsafe { lua.drop_ref(self) };
}
}
}
}
impl PartialEq for ValueRef {
fn eq(&self, other: &Self) -> bool {
assert!(
self.lua == other.lua,
"Lua instance passed Value created from a different main Lua state"
);
let lua = self.lua.lock();
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
}
}
+210 -285
View File
@@ -2,9 +2,19 @@ use std::any::TypeId;
use std::ffi::CStr;
use std::fmt;
use std::hash::Hash;
use std::os::raw::{c_char, c_int, c_void};
use std::os::raw::{c_char, c_void};
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::Lua;
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{MaybeSend, ValueRef};
use crate::util::{check_stack, get_userdata, push_string, take_userdata, StackGuard};
use crate::value::Value;
#[cfg(feature = "async")]
use std::future::Future;
@@ -14,31 +24,16 @@ use {
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, LuaGuard};
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::types::{MaybeSend, SubtypeId, ValueRef};
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
// Re-export for convenience
pub(crate) use cell::UserDataVariant;
pub(crate) use cell::UserDataStorage;
pub use cell::{UserDataRef, UserDataRefMut};
pub use ext::AnyUserDataExt;
pub(crate) use registry::UserDataProxy;
pub use registry::UserDataRegistry;
#[cfg(feature = "lua54")]
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
/// Kinds of metamethods that can be overridden.
///
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
///
/// [`UserData`]: crate::UserData
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum MetaMethod {
@@ -132,7 +127,7 @@ pub enum MetaMethod {
///
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
///
/// More information about to-be-closed variabled can be found in the Lua 5.4
/// More information about to-be-closed variables can be found in the Lua 5.4
/// [documentation][lua_doc].
///
/// Requires `feature = "lua54"`
@@ -246,9 +241,7 @@ impl AsRef<str> for MetaMethod {
}
/// Method registry for [`UserData`] implementors.
///
/// [`UserData`]: crate::UserData
pub trait UserDataMethods<'a, T> {
pub trait UserDataMethods<T> {
/// Add a regular method which accepts a `&T` as the first parameter.
///
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
@@ -258,7 +251,7 @@ pub trait UserDataMethods<'a, T> {
/// be used as a fall-back if no regular method is found.
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
@@ -266,89 +259,84 @@ pub trait UserDataMethods<'a, T> {
///
/// Refer to [`add_method`] for more information about the implementation.
///
/// [`add_method`]: #method.add_method
/// [`add_method`]: UserDataMethods::add_method
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
/// Add an async method which accepts a `&T` as the first parameter and returns Future.
/// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
///
/// Refer to [`add_method`] for more information about the implementation.
///
/// Requires `feature = "async"`
///
/// [`add_method`]: #method.add_method
/// [`add_method`]: UserDataMethods::add_method
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti;
/// Add an async method which accepts a `&mut T` as the first parameter and returns Future.
/// Add an async method which accepts a `&mut T` as the first parameter and returns [`Future`].
///
/// Refer to [`add_method`] for more information about the implementation.
///
/// Requires `feature = "async"`
///
/// [`add_method`]: #method.add_method
/// [`add_method`]: UserDataMethods::add_method
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti;
/// Add a regular method as a function which accepts generic arguments, the first argument will
/// be a [`AnyUserData`] of type `T` if the method is called with Lua method syntax:
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
/// Add a regular method as a function which accepts generic arguments.
///
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
///
/// [`AnyUserData`]: crate::AnyUserData
/// [`add_method`]: #method.add_method
/// [`add_method_mut`]: #method.add_method_mut
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
/// Add a regular method as a mutable function which accepts generic arguments.
///
/// This is a version of [`add_function`] that accepts a FnMut argument.
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
///
/// [`add_function`]: #method.add_function
/// [`add_function`]: UserDataMethods::add_function
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
/// Add a regular method as an async function which accepts generic arguments
/// and returns Future.
/// Add a regular method as an async function which accepts generic arguments and returns
/// [`Future`].
///
/// This is an async version of [`add_function`].
///
/// Requires `feature = "async"`
///
/// [`add_function`]: #method.add_function
/// [`add_function`]: UserDataMethods::add_function
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
FR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti;
/// Add a metamethod which accepts a `&T` as the first parameter.
@@ -358,10 +346,10 @@ pub trait UserDataMethods<'a, T> {
/// This can cause an error with certain binary metamethods that can trigger if only the right
/// side has a metatable. To prevent this, use [`add_meta_function`].
///
/// [`add_meta_function`]: #method.add_meta_function
/// [`add_meta_function`]: UserDataMethods::add_meta_function
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
@@ -372,45 +360,46 @@ pub trait UserDataMethods<'a, T> {
/// This can cause an error with certain binary metamethods that can trigger if only the right
/// side has a metatable. To prevent this, use [`add_meta_function`].
///
/// [`add_meta_function`]: #method.add_meta_function
/// [`add_meta_function`]: UserDataMethods::add_meta_function
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
/// Add an async metamethod which accepts a `&T` as the first parameter and returns Future.
/// Add an async metamethod which accepts a `&T` as the first parameter and returns [`Future`].
///
/// This is an async version of [`add_meta_method`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_method`]: #method.add_meta_method
/// [`add_meta_method`]: UserDataMethods::add_meta_method
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti;
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns Future.
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns
/// [`Future`].
///
/// This is an async version of [`add_meta_method_mut`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_method_mut`]: #method.add_meta_method_mut
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti;
/// Add a metamethod which accepts generic arguments.
@@ -420,54 +409,52 @@ pub trait UserDataMethods<'a, T> {
/// userdata of type `T`.
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
/// Add a metamethod as a mutable function which accepts generic arguments.
///
/// This is a version of [`add_meta_function`] that accepts a FnMut argument.
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
///
/// [`add_meta_function`]: #method.add_meta_function
/// [`add_meta_function`]: UserDataMethods::add_meta_function
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti;
/// Add a metamethod which accepts generic arguments and returns Future.
/// Add a metamethod which accepts generic arguments and returns [`Future`].
///
/// This is an async version of [`add_meta_function`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_function`]: #method.add_meta_function
/// [`add_meta_function`]: UserDataMethods::add_meta_function
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
FR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti;
}
/// Field registry for [`UserData`] implementors.
///
/// [`UserData`]: crate::UserData
pub trait UserDataFields<'a, T> {
/// Add a static field to the `UserData`.
pub trait UserDataFields<T> {
/// Add a static field to the [`UserData`].
///
/// Static fields are implemented by updating the `__index` metamethod and returning the
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
///
/// Static fields are usually shared between all instances of the `UserData` of the same type.
/// Static fields are usually shared between all instances of the [`UserData`] of the same type.
///
/// If `add_meta_method` is used to set the `__index` metamethod, it will
/// be used as a fall-back if no regular field or method are found.
fn add_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + Clone + 'static;
V: IntoLua + 'static;
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
///
@@ -478,7 +465,7 @@ pub trait UserDataFields<'a, T> {
/// be used as a fall-back if no regular field or method are found.
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua;
/// Add a regular field setter as a method which accepts a `&mut T` as the first parameter.
@@ -491,36 +478,26 @@ pub trait UserDataFields<'a, T> {
/// will be used as a fall-back if no regular field is found.
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
A: FromLua;
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
/// argument.
///
/// Prefer to use [`add_field_method_get`] as it is easier to use.
///
/// [`AnyUserData`]: crate::AnyUserData
/// [`add_field_method_get`]: #method.add_field_method_get
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
R: IntoLua;
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
/// first argument.
///
/// Prefer to use [`add_field_method_set`] as it is easier to use.
///
/// [`AnyUserData`]: crate::AnyUserData
/// [`add_field_method_set`]: #method.add_field_method_set
fn add_field_function_set<F, A>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
A: FromLua;
/// Add a metatable field.
///
/// This will initialize the metatable field with `value` on `UserData` creation.
/// This will initialize the metatable field with `value` on [`UserData`] creation.
///
/// # Note
///
@@ -528,11 +505,11 @@ pub trait UserDataFields<'a, T> {
/// like `__gc` or `__metatable`.
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + Clone + 'static;
V: IntoLua + 'static;
/// Add a metatable field computed from `f`.
///
/// This will initialize the metatable field from `f` on `UserData` creation.
/// This will initialize the metatable field from `f` on [`UserData`] creation.
///
/// # Note
///
@@ -540,13 +517,14 @@ pub trait UserDataFields<'a, T> {
/// like `__gc` or `__metatable`.
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
where
F: Fn(&'a Lua) -> Result<R> + MaybeSend + 'static,
F: FnOnce(&Lua) -> Result<R> + 'static,
R: IntoLua;
}
/// Trait for custom userdata types.
///
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
///
/// Implementation of [`IntoLua`] is automatically provided, [`FromLua`] needs to be implemented
/// manually.
///
@@ -579,12 +557,12 @@ pub trait UserDataFields<'a, T> {
/// struct MyUserData(i32);
///
/// impl UserData for MyUserData {
/// fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
/// fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
/// }
///
/// fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
/// methods.add_method_mut("add", |_, this, value: i32| {
/// fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
/// methods.add_method_mut("add", |_, mut this, value: i32| {
/// this.0 += value;
/// Ok(())
/// });
@@ -606,19 +584,14 @@ pub trait UserDataFields<'a, T> {
/// # Ok(())
/// # }
/// ```
///
/// [`IntoLua`]: crate::IntoLua
/// [`FromLua`]: crate::FromLua
/// [`UserDataFields`]: crate::UserDataFields
/// [`UserDataMethods`]: crate::UserDataMethods
pub trait UserData: Sized {
/// Adds custom fields specific to this userdata.
#[allow(unused_variables)]
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {}
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {}
/// Adds custom methods and operators specific to this userdata.
#[allow(unused_variables)]
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {}
/// Registers this type for use in Lua.
///
@@ -632,52 +605,74 @@ pub trait UserData: Sized {
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
///
/// Similar to `std::any::Any`, this provides an interface for dynamic type checking via the [`is`]
/// and [`borrow`] methods.
///
/// Internally, instances are stored in a `RefCell`, to best match the mutable semantics of the Lua
/// language.
/// Similar to [`std::any::Any`], this provides an interface for dynamic type checking via the
/// [`is`] and [`borrow`] methods.
///
/// # Note
///
/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
/// methods which check the type and acquire a borrow behind the scenes.
///
/// [`UserData`]: crate::UserData
/// [`is`]: crate::AnyUserData::is
/// [`borrow`]: crate::AnyUserData::borrow
#[derive(Clone, Debug)]
pub struct AnyUserData(pub(crate) ValueRef, pub(crate) SubtypeId);
#[derive(Clone, Debug, PartialEq)]
pub struct AnyUserData(pub(crate) ValueRef);
impl AnyUserData {
/// Checks whether the type of this userdata is `T`.
#[inline]
pub fn is<T: 'static>(&self) -> bool {
self.inspect::<T, _, _>(|_, _| Ok(())).is_ok()
self.inspect::<T, _, _>(|_| Ok(())).is_ok()
}
/// Borrow this userdata immutably if it is of type `T`.
///
/// # Errors
///
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
/// Returns a [`UserDataBorrowError`] if the userdata is already mutably borrowed.
/// Returns a [`DataTypeMismatch`] if the userdata is not of type `T` or if it's
/// scoped.
///
/// [`UserDataBorrowError`]: crate::Error::UserDataBorrowError
/// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
self.inspect(|variant, guard| variant.try_make_ref(guard))
self.inspect(|ud| ud.try_borrow_owned())
}
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
/// to the closure.
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud)))
}
/// Borrow this userdata mutably if it is of type `T`.
///
/// # Errors
///
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
/// Returns a [`UserDataBorrowMutError`] if the userdata cannot be mutably borrowed.
/// Returns a [`UserDataTypeMismatch`] if the userdata is not of type `T` or if it's
/// scoped.
///
/// [`UserDataBorrowMutError`]: crate::Error::UserDataBorrowMutError
/// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
self.inspect(|variant, guard| variant.try_make_mut_ref(guard))
self.inspect(|ud| ud.try_borrow_owned_mut())
}
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
/// to the closure.
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud)))
}
/// Takes the value out of this userdata.
///
/// Sets the special "destructed" metatable that prevents any further operations with this
/// userdata.
///
@@ -693,24 +688,49 @@ impl AnyUserData {
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
// Try to borrow userdata exclusively
let _ = (*get_userdata::<UserDataVariant<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataVariant<T>>(state).into_inner()
let _ = (*get_userdata::<UserDataStorage<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataStorage<T>>(state).into_inner()
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
/// Sets an associated value to this `AnyUserData`.
/// Destroys this userdata.
///
/// This is similar to [`AnyUserData::take`], but it doesn't require a type.
///
/// This method works for non-scoped userdata only.
pub fn destroy(&self) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_userdata_ref(&self.0)?;
protect_lua!(state, 1, 1, fn(state) {
if ffi::luaL_callmeta(state, -1, cstr!("__gc")) == 0 {
ffi::lua_pushboolean(state, 0);
}
})?;
if ffi::lua_isboolean(state, -1) != 0 && ffi::lua_toboolean(state, -1) != 0 {
return Ok(());
}
Err(Error::UserDataBorrowMutError)
}
}
/// Sets an associated value to this [`AnyUserData`].
///
/// The value may be any Lua value whatsoever, and can be retrieved with [`user_value`].
///
/// This is the same as calling [`set_nth_user_value`] with `n` set to 1.
///
/// [`user_value`]: #method.user_value
/// [`set_nth_user_value`]: #method.set_nth_user_value
/// [`user_value`]: AnyUserData::user_value
/// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
#[inline]
pub fn set_user_value<V: IntoLua>(&self, v: V) -> Result<()> {
pub fn set_user_value(&self, v: impl IntoLua) -> Result<()> {
self.set_nth_user_value(1, v)
}
@@ -718,30 +738,22 @@ impl AnyUserData {
///
/// This is the same as calling [`nth_user_value`] with `n` set to 1.
///
/// [`set_user_value`]: #method.set_user_value
/// [`nth_user_value`]: #method.nth_user_value
/// [`set_user_value`]: AnyUserData::set_user_value
/// [`nth_user_value`]: AnyUserData::nth_user_value
#[inline]
pub fn user_value<V: FromLua>(&self) -> Result<V> {
self.nth_user_value(1)
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `user_value` instead")]
pub fn get_user_value<V: FromLua>(&self) -> Result<V> {
self.nth_user_value(1)
}
/// Sets an associated `n`th value to this `AnyUserData`.
/// Sets an associated `n`th value to this [`AnyUserData`].
///
/// The value may be any Lua value whatsoever, and can be retrieved with [`nth_user_value`].
/// `n` starts from 1 and can be up to 65535.
///
/// This is supported for all Lua versions.
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
/// For other Lua versions this functionality is provided using a wrapping table.
/// This is supported for all Lua versions using a wrapping table.
///
/// [`nth_user_value`]: #method.nth_user_value
pub fn set_nth_user_value<V: IntoLua>(&self, n: usize, v: V) -> Result<()> {
/// [`nth_user_value`]: AnyUserData::nth_user_value
pub fn set_nth_user_value(&self, n: usize, v: impl IntoLua) -> Result<()> {
if n < 1 || n > u16::MAX as usize {
return Err(Error::runtime("user value index out of bounds"));
}
@@ -755,29 +767,16 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
lua.push(v)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_setiuservalue(state, -2, n as c_int);
return Ok(());
}
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushvalue(state, -2);
#[cfg(feature = "lua54")]
ffi::lua_rawseti(state, -2, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
})?;
@@ -789,11 +788,9 @@ impl AnyUserData {
///
/// `n` starts from 1 and can be up to 65535.
///
/// This is supported for all Lua versions.
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
/// For other Lua versions this functionality is provided using a wrapping table.
/// This is supported for all Lua versions using a wrapping table.
///
/// [`set_nth_user_value`]: #method.set_nth_user_value
/// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
pub fn nth_user_value<V: FromLua>(&self, n: usize) -> Result<V> {
if n < 1 || n > u16::MAX as usize {
return Err(Error::runtime("user value index out of bounds"));
@@ -807,40 +804,22 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_getiuservalue(state, -1, n as c_int);
return V::from_lua(lua.pop_value(), lua.lua());
}
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
#[cfg(feature = "lua54")]
ffi::lua_rawgeti(state, -1, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
})?;
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
return V::from_lua(Value::Nil, lua.lua());
}
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
V::from_lua(lua.pop_value(), lua.lua())
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `nth_user_value` instead")]
pub fn get_nth_user_value<V: FromLua>(&self, n: usize) -> Result<V> {
self.nth_user_value(n)
}
/// Sets an associated value to this `AnyUserData` by name.
/// Sets an associated value to this [`AnyUserData`] by name.
///
/// The value can be retrieved with [`named_user_value`].
///
/// [`named_user_value`]: #method.named_user_value
pub fn set_named_user_value<V: IntoLua>(&self, name: &str, v: V) -> Result<()> {
/// [`named_user_value`]: AnyUserData::named_user_value
pub fn set_named_user_value(&self, name: &str, v: impl IntoLua) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -852,15 +831,11 @@ impl AnyUserData {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
@@ -874,7 +849,7 @@ impl AnyUserData {
/// Returns an associated value by name set by [`set_named_user_value`].
///
/// [`set_named_user_value`]: #method.set_named_user_value
/// [`set_named_user_value`]: AnyUserData::set_named_user_value
pub fn named_user_value<V: FromLua>(&self, name: &str) -> Result<V> {
let lua = self.0.lua.lock();
let state = lua.state();
@@ -885,39 +860,34 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
ffi::lua_rawget(state, -2);
})?;
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
return V::from_lua(Value::Nil, lua.lua());
}
push_string(state, name.as_bytes(), !lua.unlikely_memory_error())?;
ffi::lua_rawget(state, -2);
V::from_lua(lua.pop_value(), lua.lua())
V::from_stack(-1, &lua)
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `named_user_value` instead")]
pub fn get_named_user_value<V: FromLua>(&self, name: &str) -> Result<V> {
self.named_user_value(name)
}
/// Returns a metatable of this `UserData`.
/// Returns a metatable of this [`AnyUserData`].
///
/// Returned [`UserDataMetatable`] object wraps the original metatable and
/// provides safe access to its methods.
///
/// For `T: 'static` returned metatable is shared among all instances of type `T`.
///
/// [`UserDataMetatable`]: crate::UserDataMetatable
#[inline]
pub fn get_metatable(&self) -> Result<UserDataMetatable> {
self.get_raw_metatable().map(UserDataMetatable)
pub fn metatable(&self) -> Result<UserDataMetatable> {
self.raw_metatable().map(UserDataMetatable)
}
fn get_raw_metatable(&self) -> Result<Table> {
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "please use `metatable` instead")]
pub fn get_metatable(&self) -> Result<UserDataMetatable> {
self.metatable()
}
fn raw_metatable(&self) -> Result<Table> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -940,23 +910,8 @@ impl AnyUserData {
self.0.to_pointer()
}
#[cfg(feature = "async")]
#[inline]
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
let lua = self.0.lua.lock();
unsafe { lua.get_userdata_ref_type_id(&self.0) }
}
/// Returns a type name of this `UserData` (from a metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
match self.1 {
SubtypeId::None => {}
#[cfg(feature = "luau")]
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
#[cfg(feature = "luajit")]
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
}
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -979,56 +934,50 @@ impl AnyUserData {
}
}
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
pub(crate) fn equals(&self, other: &Self) -> Result<bool> {
// Uses lua_rawequal() under the hood
if self == other {
return Ok(true);
}
let mt = self.get_raw_metatable()?;
if mt != other.get_raw_metatable()? {
let mt = self.raw_metatable()?;
if mt != other.raw_metatable()? {
return Ok(false);
}
if mt.contains_key("__eq")? {
return mt.get::<_, Function>("__eq")?.call((self, other));
return mt.get::<Function>("__eq")?.call((self, other));
}
Ok(false)
}
/// Returns `true` if this `AnyUserData` is serializable (eg. was created using
/// `create_ser_userdata`).
/// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
/// [`Lua::create_ser_userdata`]).
#[cfg(feature = "serialize")]
pub(crate) fn is_serializable(&self) -> bool {
let lua = self.0.lua.lock();
let is_serializable = || unsafe {
// Userdata must be registered and not destructed
let _ = lua.get_userdata_ref_type_id(&self.0)?;
let ud = &*get_userdata::<UserDataVariant<()>>(lua.ref_thread(), self.0.index);
match ud {
UserDataVariant::Serializable(..) => Result::Ok(true),
_ => Result::Ok(false),
}
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
Ok::<_, Error>((*ud).is_serializable())
};
is_serializable().unwrap_or(false)
}
pub(crate) fn inspect<'a, T, F, R>(&'a self, func: F) -> Result<R>
pub(crate) fn inspect<T, F, R>(&self, func: F) -> Result<R>
where
T: 'static,
F: FnOnce(&'a UserDataVariant<T>, LuaGuard) -> Result<R>,
F: FnOnce(&UserDataStorage<T>) -> Result<R>,
{
let lua = self.0.lua.lock();
unsafe {
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ref_thread = lua.ref_thread();
let ud = get_userdata::<UserDataVariant<T>>(ref_thread, self.0.index);
func(&*ud, lua)
let ud = get_userdata::<UserDataStorage<T>>(lua.ref_thread(), self.0.index);
func(&*ud)
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -1036,27 +985,7 @@ impl AnyUserData {
}
}
impl PartialEq for AnyUserData {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl AsRef<AnyUserData> for AnyUserData {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
#[cfg(feature = "lua54")]
return ffi::lua_getiuservalue(state, idx, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
return ffi::lua_getuservalue(state, idx);
}
/// Handle to a `UserData` metatable.
/// Handle to a [`AnyUserData`] metatable.
#[derive(Clone, Debug)]
pub struct UserDataMetatable(pub(crate) Table);
@@ -1075,7 +1004,7 @@ impl UserDataMetatable {
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
/// Setting `__index` or `__newindex` metamethods is also restricted because their values are
/// cached for `mlua` internal usage.
pub fn set<V: IntoLua>(&self, key: impl AsRef<str>, value: V) -> Result<()> {
pub fn set(&self, key: impl AsRef<str>, value: impl IntoLua) -> Result<()> {
let key = MetaMethod::validate(key.as_ref())?;
// `__index` and `__newindex` cannot be changed in runtime, because values are cached
if key == MetaMethod::Index || key == MetaMethod::NewIndex {
@@ -1099,17 +1028,14 @@ impl UserDataMetatable {
}
}
/// An iterator over the pairs of a [`UserData`] metatable.
/// An iterator over the pairs of a [`AnyUserData`] metatable.
///
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
///
/// This struct is created by the [`UserDataMetatable::pairs`] method.
///
/// [`UserData`]: crate::UserData
/// [`UserDataMetatable::pairs`]: crate::UserDataMetatable::method.pairs
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
impl<'a, V> Iterator for UserDataMetatablePairs<'a, V>
impl<V> Iterator for UserDataMetatablePairs<'_, V>
where
V: FromLua,
{
@@ -1137,24 +1063,11 @@ impl Serialize for AnyUserData {
S: Serializer,
{
let lua = self.0.lua.lock();
// Special case for Luau buffer type
#[cfg(feature = "luau")]
if self.1 == SubtypeId::Buffer {
let buf = unsafe {
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
std::slice::from_raw_parts(buf as *const u8, size)
};
return serializer.serialize_bytes(buf);
}
unsafe {
let _ = lua
.get_userdata_ref_type_id(&self.0)
.map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataVariant<()>>(lua.ref_thread(), self.0.index);
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
ud.serialize(serializer)
}
}
@@ -1165,10 +1078,20 @@ pub(crate) struct WrappedUserdata<F: FnOnce(&Lua) -> Result<AnyUserData>>(F);
impl AnyUserData {
/// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
///
/// This function uses [`Lua::create_any_userdata()`] under the hood.
/// This function uses [`Lua::create_any_userdata`] under the hood.
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
WrappedUserdata(move |lua| lua.create_any_userdata(data))
}
/// Wraps any Rust type that implements [`Serialize`], returning an opaque type that implements
/// [`IntoLua`] trait.
///
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
}
}
impl<F> IntoLua for WrappedUserdata<F>
@@ -1181,8 +1104,10 @@ where
}
mod cell;
mod ext;
mod lock;
mod object;
mod registry;
mod util;
#[cfg(test)]
mod assertions {
+292 -162
View File
@@ -1,89 +1,110 @@
use std::any::{type_name, TypeId};
use std::cell::{Cell, UnsafeCell};
use std::cell::{Cell, RefCell, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::rc::Rc;
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, Serializer};
use crate::error::{Error, Result};
use crate::state::{Lua, LuaGuard, RawLua};
use crate::state::{Lua, RawLua};
use crate::traits::FromLua;
use crate::types::XRc;
use crate::userdata::AnyUserData;
use crate::util::get_userdata;
use crate::value::{FromLua, Value};
use crate::value::Value;
use super::lock::{RawLock, UserDataLock};
use super::util::is_sync;
#[cfg(all(feature = "serialize", not(feature = "send")))]
type DynSerialize = dyn erased_serde::Serialize;
#[cfg(all(feature = "serialize", feature = "send"))]
type DynSerialize = dyn erased_serde::Serialize + Send;
pub(crate) enum UserDataStorage<T> {
Owned(UserDataVariant<T>),
Scoped(ScopedUserDataVariant<T>),
}
// A enum for storing userdata values.
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
pub(crate) enum UserDataVariant<T> {
Default(Rc<InnerRefCell<T>>),
Default(XRc<UserDataCell<T>>),
#[cfg(feature = "serialize")]
Serializable(Rc<InnerRefCell<Box<dyn erased_serde::Serialize>>>),
Serializable(XRc<UserDataCell<Box<DynSerialize>>>),
}
impl<T> Clone for UserDataVariant<T> {
#[inline]
fn clone(&self) -> Self {
match self {
Self::Default(inner) => Self::Default(Rc::clone(inner)),
Self::Default(inner) => Self::Default(XRc::clone(inner)),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => UserDataVariant::Serializable(Rc::clone(inner)),
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
}
}
}
impl<T> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Default(Rc::new(InnerRefCell::new(data)))
}
// Immutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) unsafe fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
UserDataBorrowRef::try_from(self)
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_make_ref(&self, guard: LuaGuard) -> Result<UserDataRef<T>> {
UserDataRef::try_from(self.clone(), guard)
fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
UserDataRef::try_from(self.clone())
}
// Mutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) unsafe fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
UserDataBorrowMut::try_from(self)
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_make_mut_ref(&self, guard: LuaGuard) -> Result<UserDataRefMut<T>> {
UserDataRefMut::try_from(self.clone(), guard)
fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
UserDataRefMut::try_from(self.clone())
}
// Returns the wrapped value.
//
// This method checks that we have exclusive access to the value.
pub(crate) fn into_inner(self) -> Result<T> {
set_writing(self.flag())?;
fn into_inner(self) -> Result<T> {
if !self.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
Ok(match self {
Self::Default(inner) => Rc::into_inner(inner).unwrap().value.into_inner(),
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => unsafe {
let raw = Box::into_raw(Rc::into_inner(inner).unwrap().value.into_inner());
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
*Box::from_raw(raw as *mut T)
},
})
}
#[inline(always)]
fn flag(&self) -> &Cell<BorrowFlag> {
fn raw_lock(&self) -> &RawLock {
match self {
Self::Default(inner) => &inner.borrow,
Self::Default(inner) => &inner.raw_lock,
#[cfg(feature = "serialize")]
Self::Serializable(inner) => &inner.borrow,
Self::Serializable(inner) => &inner.raw_lock,
}
}
#[inline(always)]
fn borrow_count(&self) -> &Cell<usize> {
match self {
Self::Default(inner) => &inner.borrow_count,
#[cfg(feature = "serialize")]
Self::Serializable(inner) => &inner.borrow_count,
}
}
@@ -98,68 +119,68 @@ impl<T> UserDataVariant<T> {
}
#[cfg(feature = "serialize")]
impl<T: Serialize + 'static> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self {
let data = Box::new(data) as Box<dyn erased_serde::Serialize>;
Self::Serializable(Rc::new(InnerRefCell::new(data)))
}
}
#[cfg(feature = "serialize")]
impl Serialize for UserDataVariant<()> {
impl Serialize for UserDataStorage<()> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
UserDataVariant::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
UserDataVariant::Serializable(inner) => unsafe {
let _ = self.try_borrow().map_err(serde::ser::Error::custom)?;
Self::Owned(UserDataVariant::Serializable(inner)) => unsafe {
// We need to borrow the inner value exclusively to serialize it.
#[cfg(feature = "send")]
let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?;
// No need to do this if the `send` feature is disabled.
#[cfg(not(feature = "send"))]
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
(*inner.value.get()).serialize(serializer)
},
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
}
}
}
//
// Inspired by `std::cell::RefCell`` implementation
//
pub(crate) struct InnerRefCell<T> {
borrow: Cell<BorrowFlag>,
/// A type that provides interior mutability for a userdata value (thread-safe).
pub(crate) struct UserDataCell<T> {
raw_lock: RawLock,
borrow_count: Cell<usize>,
value: UnsafeCell<T>,
}
impl<T> InnerRefCell<T> {
#[cfg(feature = "send")]
unsafe impl<T: Send> Send for UserDataCell<T> {}
#[cfg(feature = "send")]
unsafe impl<T: Send> Sync for UserDataCell<T> {}
impl<T> UserDataCell<T> {
#[inline(always)]
pub fn new(value: T) -> Self {
InnerRefCell {
borrow: Cell::new(UNUSED),
fn new(value: T) -> Self {
UserDataCell {
raw_lock: RawLock::INIT,
borrow_count: Cell::new(0),
value: UnsafeCell::new(value),
}
}
}
/// A wrapper type for a [`UserData`] value that provides read access.
/// A wrapper type for a userdata value that provides read access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRef<T> {
variant: UserDataVariant<T>,
#[allow(unused)]
guard: LuaGuard,
}
pub struct UserDataRef<T>(UserDataVariant<T>);
impl<T> Deref for UserDataRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.variant.as_ptr() }
unsafe { &*self.0.as_ptr() }
}
}
impl<T> Drop for UserDataRef<T> {
#[inline]
fn drop(&mut self) {
unset_reading(self.variant.flag());
if !cfg!(feature = "send") || is_sync::<T>() {
unsafe { self.0.raw_lock().unlock_shared() };
} else {
unsafe { self.0.raw_lock().unlock_exclusive() };
}
}
}
@@ -175,11 +196,19 @@ impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
}
}
impl<T> UserDataRef<T> {
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
type Error = Error;
#[inline]
fn try_from(variant: UserDataVariant<T>, guard: LuaGuard) -> Result<Self> {
set_reading(variant.flag())?;
Ok(UserDataRef { variant, guard })
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
if !cfg!(feature = "send") || is_sync::<T>() {
if !variant.raw_lock().try_lock_shared() {
return Err(Error::UserDataBorrowError);
}
} else if !variant.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowError);
}
Ok(UserDataRef(variant))
}
}
@@ -189,46 +218,41 @@ impl<T: 'static> FromLua for UserDataRef<T> {
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id(idx)?;
let type_id = lua.get_userdata_type_id::<T>(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let guard = lua.lua().lock_arc();
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_make_ref(guard)
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned()
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
/// A wrapper type for a userdata value that provides read and write access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRefMut<T> {
variant: UserDataVariant<T>,
#[allow(unused)]
guard: LuaGuard,
}
pub struct UserDataRefMut<T>(UserDataVariant<T>);
impl<T> Deref for UserDataRefMut<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.variant.as_ptr() }
unsafe { &*self.0.as_ptr() }
}
}
impl<T> DerefMut for UserDataRefMut<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.variant.as_ptr() }
unsafe { &mut *self.0.as_ptr() }
}
}
impl<T> Drop for UserDataRefMut<T> {
#[inline]
fn drop(&mut self) {
unset_writing(self.variant.flag());
unsafe { self.0.raw_lock().unlock_exclusive() };
}
}
@@ -244,11 +268,15 @@ impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
}
}
impl<T> UserDataRefMut<T> {
fn try_from(variant: UserDataVariant<T>, guard: LuaGuard) -> Result<Self> {
// There must currently be no existing references
set_writing(variant.flag())?;
Ok(UserDataRefMut { variant, guard })
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
type Error = Error;
#[inline]
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
if !variant.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
Ok(UserDataRefMut(variant))
}
}
@@ -258,84 +286,35 @@ impl<T: 'static> FromLua for UserDataRefMut<T> {
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id(idx)?;
let type_id = lua.get_userdata_type_id::<T>(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let guard = lua.lua().lock_arc();
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_make_mut_ref(guard)
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned_mut()
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
// Positive values represent the number of `Ref` active. Negative values
// represent the number of `RefMut` active. Multiple `RefMut`s can only be
// active at a time if they refer to distinct, nonoverlapping components of a
// `RefCell` (e.g., different ranges of a slice).
type BorrowFlag = isize;
const UNUSED: BorrowFlag = 0;
#[inline(always)]
fn is_writing(x: BorrowFlag) -> bool {
x < UNUSED
}
#[inline(always)]
fn is_reading(x: BorrowFlag) -> bool {
x > UNUSED
}
#[inline(always)]
fn set_writing(borrow: &Cell<BorrowFlag>) -> Result<()> {
let flag = borrow.get();
if flag != UNUSED {
return Err(Error::UserDataBorrowMutError);
}
borrow.set(UNUSED - 1);
Ok(())
}
#[inline(always)]
fn set_reading(borrow: &Cell<BorrowFlag>) -> Result<()> {
let flag = borrow.get().wrapping_add(1);
if !is_reading(flag) {
return Err(Error::UserDataBorrowError);
}
borrow.set(flag);
Ok(())
}
#[inline(always)]
#[track_caller]
fn unset_writing(borrow: &Cell<BorrowFlag>) {
let flag = borrow.get();
debug_assert!(is_writing(flag));
borrow.set(flag + 1);
}
#[inline(always)]
#[track_caller]
fn unset_reading(borrow: &Cell<BorrowFlag>) {
let flag = borrow.get();
debug_assert!(is_reading(flag));
borrow.set(flag - 1);
}
/// A type that provides read access to a userdata value (borrowing the value).
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
impl<'a, T> Drop for UserDataBorrowRef<'a, T> {
impl<T> Drop for UserDataBorrowRef<'_, T> {
#[inline]
fn drop(&mut self) {
unset_reading(self.0.flag());
unsafe {
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
self.0.raw_lock().unlock_shared();
}
}
}
impl<'a, T> Deref for UserDataBorrowRef<'a, T> {
impl<T> Deref for UserDataBorrowRef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
// SAFETY: `UserDataBorrowRef` is only created with shared access to the value.
unsafe { &*self.0.as_ptr() }
}
}
@@ -345,29 +324,31 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
#[inline(always)]
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
set_reading(variant.flag())?;
// We don't need to check for `T: Sync` because when this method is used (internally),
// Lua mutex is already locked.
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
// exclusively locked.
if !variant.raw_lock().try_lock_shared() {
return Err(Error::UserDataBorrowError);
}
variant.borrow_count().set(variant.borrow_count().get() + 1);
Ok(UserDataBorrowRef(variant))
}
}
impl<'a, T> UserDataBorrowRef<'a, T> {
#[inline(always)]
pub(crate) fn get_ref(&self) -> &'a T {
// SAFETY: `UserDataBorrowRef` is only created when the borrow flag is set to reading.
unsafe { &*self.0.as_ptr() }
}
}
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
impl<'a, T> Drop for UserDataBorrowMut<'a, T> {
impl<T> Drop for UserDataBorrowMut<'_, T> {
#[inline]
fn drop(&mut self) {
unset_writing(self.0.flag());
unsafe {
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
self.0.raw_lock().unlock_exclusive();
}
}
}
impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
impl<T> Deref for UserDataBorrowMut<'_, T> {
type Target = T;
#[inline]
@@ -376,7 +357,7 @@ impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
}
}
impl<'a, T> DerefMut for UserDataBorrowMut<'a, T> {
impl<T> DerefMut for UserDataBorrowMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.as_ptr() }
@@ -388,35 +369,184 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
#[inline(always)]
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
set_writing(variant.flag())?;
if !variant.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
variant.borrow_count().set(variant.borrow_count().get() + 1);
Ok(UserDataBorrowMut(variant))
}
}
impl<'a, T> UserDataBorrowMut<'a, T> {
#[inline(always)]
pub(crate) fn get_mut(&mut self) -> &'a mut T {
// SAFETY: `UserDataBorrowMut` is only created when the borrow flag is set to writing.
unsafe { &mut *self.0.as_ptr() }
}
}
#[inline]
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
match value {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
to: "userdata".to_string(),
message: Some(format!("expected userdata of type {}", type_name::<T>())),
}),
}
}
pub(crate) enum ScopedUserDataVariant<T> {
Ref(*const T),
RefMut(RefCell<*mut T>),
Boxed(RefCell<*mut T>),
}
impl<T> Drop for ScopedUserDataVariant<T> {
#[inline]
fn drop(&mut self) {
if let Self::Boxed(value) = self {
if let Ok(value) = value.try_borrow_mut() {
unsafe { drop(Box::from_raw(*value)) };
}
}
}
}
impl<T: 'static> UserDataStorage<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
}
#[inline(always)]
pub(crate) fn new_ref(data: &T) -> Self {
Self::Scoped(ScopedUserDataVariant::Ref(data))
}
#[inline(always)]
pub(crate) fn new_ref_mut(data: &mut T) -> Self {
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self
where
T: Serialize + crate::types::MaybeSend,
{
let data = Box::new(data) as Box<DynSerialize>;
Self::Owned(UserDataVariant::Serializable(XRc::new(UserDataCell::new(data))))
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn is_serializable(&self) -> bool {
matches!(self, Self::Owned(UserDataVariant::Serializable(_)))
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
match self {
Self::Owned(data) => data.try_borrow_owned(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[allow(unused)]
#[inline(always)]
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
match self {
Self::Owned(data) => data.try_borrow(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_owned_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn into_inner(self) -> Result<T> {
match self {
Self::Owned(data) => data.into_inner(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
}
impl<T> UserDataStorage<T> {
#[inline(always)]
pub(crate) fn new_scoped(data: T) -> Self {
let data = Box::into_raw(Box::new(data));
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
}
#[inline(always)]
pub(crate) fn is_borrowed(&self) -> bool {
match self {
Self::Owned(variant) => variant.borrow_count().get() > 0,
Self::Scoped(_) => true,
}
}
#[inline]
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&*data.try_borrow()?)),
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(f(unsafe { &**t }))
}
}
}
#[inline]
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&mut *data.try_borrow_mut()?)),
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let mut t = value
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(unsafe { &mut **t }))
}
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_all!(UserDataRef<()>: Sync, Send);
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Sync, Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
#[cfg(feature = "send")]
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
}
-180
View File
@@ -1,180 +0,0 @@
use crate::error::{Error, Result};
use crate::private::Sealed;
use crate::userdata::{AnyUserData, MetaMethod};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
#[cfg(feature = "async")]
use std::future::Future;
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
pub trait AnyUserDataExt: Sealed {
/// Gets the value associated to `key` from the userdata, assuming it has `__index` metamethod.
fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V>;
/// Sets the value associated to `key` in the userdata, assuming it has `__newindex` metamethod.
fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()>;
/// Calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed
/// arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// Asynchronously calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed
/// arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// Calls the userdata method, assuming it has `__index` metamethod
/// and a function associated to `name`.
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti;
/// 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<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti;
}
impl AnyUserDataExt for AnyUserData {
fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Index)? {
Value::Table(table) => table.raw_get(key),
Value::Function(func) => func.call((self, key)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::NewIndex)? {
Value::Table(table) => table.raw_set(key, value),
Value::Function(func) => func.call((self, key, value)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call((self, args)),
_ => Err(Error::runtime("attempt to call a userdata value")),
}
}
#[cfg(feature = "async")]
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
let args = (self, args).into_lua_multi(lua.lua());
async move {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call_async(args?).await,
_ => Err(Error::runtime("attempt to call a userdata value")),
}
}
}
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.call_function(name, (self, args))
}
#[cfg(feature = "async")]
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.call_async_function(name, (self, args))
}
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
match self.get(name)? {
Value::Function(func) => func.call(args),
val => {
let msg = format!("attempt to call a {} value", val.type_name());
Err(Error::runtime(msg))
}
}
}
#[cfg(feature = "async")]
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
async move {
match self.get::<_, Value>(name)? {
Value::Function(func) => func.call_async(args?).await,
val => {
let msg = format!("attempt to call a {} value", val.type_name());
Err(Error::runtime(msg))
}
}
}
}
}
+94
View File
@@ -0,0 +1,94 @@
pub(crate) trait UserDataLock {
const INIT: Self;
fn try_lock_shared(&self) -> bool;
fn try_lock_exclusive(&self) -> bool;
unsafe fn unlock_shared(&self);
unsafe fn unlock_exclusive(&self);
}
pub(crate) use lock_impl::RawLock;
#[cfg(not(feature = "send"))]
#[cfg(not(tarpaulin_include))]
mod lock_impl {
use std::cell::Cell;
// Positive values represent the number of read references.
// Negative values represent the number of write references (only one allowed).
pub(crate) type RawLock = Cell<isize>;
const UNUSED: isize = 0;
impl super::UserDataLock for RawLock {
#[allow(clippy::declare_interior_mutable_const)]
const INIT: Self = Cell::new(UNUSED);
#[inline(always)]
fn try_lock_shared(&self) -> bool {
let flag = self.get().wrapping_add(1);
if flag <= UNUSED {
return false;
}
self.set(flag);
true
}
#[inline(always)]
fn try_lock_exclusive(&self) -> bool {
let flag = self.get();
if flag != UNUSED {
return false;
}
self.set(UNUSED - 1);
true
}
#[inline(always)]
unsafe fn unlock_shared(&self) {
let flag = self.get();
debug_assert!(flag > UNUSED);
self.set(flag - 1);
}
#[inline(always)]
unsafe fn unlock_exclusive(&self) {
let flag = self.get();
debug_assert!(flag < UNUSED);
self.set(flag + 1);
}
}
}
#[cfg(feature = "send")]
mod lock_impl {
use parking_lot::lock_api::RawRwLock;
pub(crate) type RawLock = parking_lot::RawRwLock;
impl super::UserDataLock for RawLock {
#[allow(clippy::declare_interior_mutable_const)]
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
#[inline(always)]
fn try_lock_shared(&self) -> bool {
RawRwLock::try_lock_shared(self)
}
#[inline(always)]
fn try_lock_exclusive(&self) -> bool {
RawRwLock::try_lock_exclusive(self)
}
#[inline(always)]
unsafe fn unlock_shared(&self) {
RawRwLock::unlock_shared(self)
}
#[inline(always)]
unsafe fn unlock_exclusive(&self) {
RawRwLock::unlock_exclusive(self)
}
}
}
+93
View File
@@ -0,0 +1,93 @@
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::table::Table;
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
use crate::userdata::AnyUserData;
use crate::value::Value;
use crate::Function;
#[cfg(feature = "async")]
use futures_util::future::{self, Either, Future};
impl ObjectLike for AnyUserData {
#[inline]
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
// `lua_gettable` method used under the hood can work with any Lua value
// that has `__index` metamethod
Table(self.0.copy()).get_protected(key)
}
#[inline]
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
// `lua_settable` method used under the hood can work with any Lua value
// that has `__newindex` metamethod
Table(self.0.copy()).set_protected(key, value)
}
#[inline]
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti,
{
Function(self.0.copy()).call(args)
}
#[cfg(feature = "async")]
#[inline]
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
R: FromLuaMulti,
{
Function(self.0.copy()).call_async(args)
}
#[inline]
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti,
{
self.call_function(name, (self, args))
}
#[cfg(feature = "async")]
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
R: FromLuaMulti,
{
self.call_async_function(name, (self, args))
}
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
where
R: FromLuaMulti,
{
match self.get(name)? {
Value::Function(func) => func.call(args),
val => {
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
Err(Error::RuntimeError(msg))
}
}
}
#[cfg(feature = "async")]
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
where
R: FromLuaMulti,
{
match self.get(name) {
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
Ok(val) => {
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
}
Err(err) => Either::Right(future::ready(Err(err))),
}
}
#[inline]
fn to_string(&self) -> Result<StdString> {
Value::UserData(AnyUserData(self.0.copy())).to_string()
}
}
+408 -175
View File
@@ -3,45 +3,85 @@
use std::any::TypeId;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::os::raw::c_void;
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::state::{Lua, RawLua};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{Callback, MaybeSend};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage};
use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use super::cell::{UserDataBorrowMut, UserDataBorrowRef, UserDataVariant};
use crate::value::Value;
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
crate::userdata::{UserDataRef, UserDataRefMut},
std::future::{self, Future},
};
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
use std::rc::Rc;
#[cfg(feature = "userdata-wrappers")]
use std::sync::{Arc, Mutex, RwLock};
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
#[derive(Clone, Copy)]
enum UserDataTypeId {
Shared(TypeId),
Unique(usize),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Rc(TypeId),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
RcRefCell(TypeId),
#[cfg(feature = "userdata-wrappers")]
Arc(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcMutex(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcRwLock(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcParkingLotMutex(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcParkingLotRwLock(TypeId),
}
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<'a, T: 'static> {
pub struct UserDataRegistry<T> {
// Fields
pub(crate) fields: Vec<(String, Callback<'a>)>,
pub(crate) field_getters: Vec<(String, Callback<'a>)>,
pub(crate) field_setters: Vec<(String, Callback<'a>)>,
pub(crate) meta_fields: Vec<(String, Callback<'a>)>,
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
pub(crate) field_getters: Vec<(String, Callback)>,
pub(crate) field_setters: Vec<(String, Callback)>,
pub(crate) meta_fields: Vec<(String, StaticFieldCallback)>,
// Methods
pub(crate) methods: Vec<(String, Callback<'a>)>,
pub(crate) methods: Vec<(String, Callback)>,
#[cfg(feature = "async")]
pub(crate) async_methods: Vec<(String, AsyncCallback<'a>)>,
pub(crate) meta_methods: Vec<(String, Callback<'a>)>,
pub(crate) async_methods: Vec<(String, AsyncCallback)>,
pub(crate) meta_methods: Vec<(String, Callback)>,
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'a>)>,
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
type_id: UserDataTypeId,
_type: PhantomData<T>,
}
impl<'a, T: 'static> UserDataRegistry<'a, T> {
pub(crate) const fn new() -> Self {
impl<T> UserDataRegistry<T> {
#[inline(always)]
pub(crate) fn new(type_id: TypeId) -> Self {
Self::with_type_id(UserDataTypeId::Shared(type_id))
}
#[inline(always)]
pub(crate) fn new_unique(ud_ptr: *mut c_void) -> Self {
Self::with_type_id(UserDataTypeId::Unique(ud_ptr as usize))
}
#[inline(always)]
fn with_type_id(type_id: UserDataTypeId) -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
@@ -53,13 +93,36 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
type_id,
_type: PhantomData,
}
}
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'a>
#[inline]
pub(crate) fn type_id(&self) -> Option<TypeId> {
match self.type_id {
UserDataTypeId::Shared(type_id) => Some(type_id),
UserDataTypeId::Unique(_) => None,
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
UserDataTypeId::Rc(type_id) => Some(type_id),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
UserDataTypeId::RcRefCell(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::Arc(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcMutex(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcRwLock(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id),
}
}
fn box_method<M, A, R>(&self, name: &str, method: M) -> Callback
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
@@ -70,6 +133,7 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
};
}
let target_type_id = self.type_id;
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
@@ -77,23 +141,114 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
}
let state = rawlua.state();
// Find absolute "self" index before processing args
let index = ffi::lua_absindex(state, -nargs);
let self_index = ffi::lua_absindex(state, -nargs);
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(index)) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(borrow_userdata_ref::<T>(state, index));
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
match target_type_id {
#[rustfmt::skip]
UserDataTypeId::Shared(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[rustfmt::skip]
UserDataTypeId::Unique(target_ptr)
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
{
let ud = target_ptr as *mut UserDataStorage<T>;
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::Rc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::RcRefCell(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::Arc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'a>
fn box_method_mut<M, A, R>(&self, name: &str, method: M) -> Callback
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
@@ -105,6 +260,7 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
}
let method = RefCell::new(method);
let target_type_id = self.type_id;
Box::new(move |rawlua, nargs| unsafe {
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
if nargs == 0 {
@@ -113,14 +269,99 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
}
let state = rawlua.state();
// Find absolute "self" index before processing args
let index = ffi::lua_absindex(state, -nargs);
let self_index = ffi::lua_absindex(state, -nargs);
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(index)) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(state, index));
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
match target_type_id {
#[rustfmt::skip]
UserDataTypeId::Shared(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[rustfmt::skip]
UserDataTypeId::Unique(target_ptr)
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
{
let ud = target_ptr as *mut UserDataStorage<T>;
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::Rc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
{
Err(Error::UserDataBorrowMutError)
},
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::RcRefCell(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::Arc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
{
Err(Error::UserDataBorrowMutError)
},
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
@@ -128,11 +369,12 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
}
#[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'a>
fn box_async_method<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
where
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
@@ -145,39 +387,34 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
};
}
Box::new(move |rawlua, mut args| unsafe {
let this = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let lua = rawlua.lua();
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
let args = A::from_lua_args(args, 2, Some(&name), lua);
let (ref_thread, index) = (rawlua.ref_thread(), this.0.index);
match try_self_arg!(this.type_id()) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(borrow_userdata_ref::<T>(ref_thread, index));
let args = match args {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = method(lua, ud.get_ref(), args);
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
}
_ => {
let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch);
Box::pin(future::ready(Err(err)))
}
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
try_self_arg!(Err(err));
}
// Stack will be empty when polling the future, keep `self` on the ref thread
let self_ud = try_self_arg!(AnyUserData::from_stack(-nargs, rawlua));
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
let self_ud = try_self_arg!(self_ud.borrow());
let args = match args {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let lua = rawlua.lua();
let fut = method(lua.clone(), self_ud, args);
// Lua is locked when the future is polled
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
})
}
#[cfg(feature = "async")]
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'a>
fn box_async_method_mut<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
where
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
@@ -190,36 +427,30 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
};
}
Box::new(move |rawlua, mut args| unsafe {
let this = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let lua = rawlua.lua();
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
let args = A::from_lua_args(args, 2, Some(&name), lua);
let (ref_thread, index) = (rawlua.ref_thread(), this.0.index);
match try_self_arg!(this.type_id()) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(ref_thread, index));
let args = match args {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = method(lua, ud.get_mut(), args);
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
}
_ => {
let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch);
Box::pin(future::ready(Err(err)))
}
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
try_self_arg!(Err(err));
}
// Stack will be empty when polling the future, keep `self` on the ref thread
let self_ud = try_self_arg!(AnyUserData::from_stack(-nargs, rawlua));
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
let self_ud = try_self_arg!(self_ud.borrow_mut());
let args = match args {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let lua = rawlua.lua();
let fut = method(lua.clone(), self_ud, args);
// Lua is locked when the future is polled
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
})
}
fn box_function<F, A, R>(name: &str, function: F) -> Callback<'a>
fn box_function<F, A, R>(&self, name: &str, function: F) -> Callback
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
@@ -230,9 +461,9 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
})
}
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'a>
fn box_function_mut<F, A, R>(&self, name: &str, function: F) -> Callback
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
@@ -248,29 +479,26 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
}
#[cfg(feature = "async")]
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'a>
fn box_async_function<F, A, FR, R>(&self, name: &str, function: F) -> AsyncCallback
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
FR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
Box::new(move |rawlua, args| unsafe {
let lua = rawlua.lua();
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
Box::new(move |rawlua, nargs| unsafe {
let args = match A::from_stack_args(nargs, 1, Some(&name), rawlua) {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = function(lua, args);
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
let lua = rawlua.lua();
let fut = function(lua.clone(), args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
})
}
pub(crate) fn check_meta_field<V>(lua: &Lua, name: &str, value: V) -> Result<Value>
where
V: IntoLua,
{
pub(crate) fn check_meta_field(lua: &Lua, name: &str, value: impl IntoLua) -> Result<Value> {
let value = value.into_lua(lua)?;
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
match value {
@@ -293,278 +521,268 @@ fn get_function_name<T>(name: &str) -> StdString {
format!("{}.{name}", short_type_name::<T>())
}
impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
impl<T> UserDataFields<T> for UserDataRegistry<T> {
fn add_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + Clone + 'static,
V: IntoLua + 'static,
{
let name = name.to_string();
let callback = Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) });
self.fields.push((name, callback));
self.fields.push((
name,
Box::new(move |rawlua| unsafe { value.push_into_stack(rawlua) }),
));
}
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_method(&name, move |lua, data, ()| method(lua, data));
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
self.field_getters.push((name, callback));
}
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.field_setters.push((name, callback));
}
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.field_getters.push((name, callback));
}
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
where
F: FnMut(&'a Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
self.field_setters.push((name, callback));
}
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + Clone + 'static,
V: IntoLua + 'static,
{
let name = name.to_string();
self.meta_fields.push((
name.clone(),
Box::new(move |lua, _| unsafe {
Self::check_meta_field(lua.lua(), &name, value.clone())?.push_into_stack_multi(lua)
Box::new(move |rawlua| unsafe {
Self::check_meta_field(rawlua.lua(), &name, value)?.push_into_stack(rawlua)
}),
));
}
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
where
F: Fn(&'a Lua) -> Result<R> + MaybeSend + 'static,
F: FnOnce(&Lua) -> Result<R> + 'static,
R: IntoLua,
{
let name = name.to_string();
self.meta_fields.push((
name.clone(),
Box::new(move |rawlua, _| unsafe {
Box::new(move |rawlua| unsafe {
let lua = rawlua.lua();
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack_multi(rawlua)
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
}),
));
}
}
impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
let callback = self.box_method(&name, method);
self.methods.push((name, callback));
}
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
let callback = self.box_async_method(&name, method);
self.async_methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
let callback = self.box_async_method_mut(&name, method);
self.async_methods.push((name, callback));
}
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.methods.push((name, callback));
}
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
let callback = self.box_function_mut(&name, function);
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
FR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
let callback = self.box_async_function(&name, function);
self.async_methods.push((name, callback));
}
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
let callback = self.box_method(&name, method);
self.meta_methods.push((name, callback));
}
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
let callback = self.box_async_method(&name, method);
self.async_meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
let callback = self.box_async_method_mut(&name, method);
self.async_meta_methods.push((name, callback));
}
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.meta_methods.push((name, callback));
}
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
let callback = self.box_function_mut(&name, function);
self.meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
FR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
let callback = self.box_async_function(&name, function);
self.async_meta_methods.push((name, callback));
}
}
// Borrow the userdata in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_ref<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowRef<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow()
}
// Borrow the userdata mutably in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_mut<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowMut<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
($type:ty => $type_variant:tt) => {
lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>()));
};
($type:ty, $type_id:expr) => {
impl<T: UserData + 'static> UserData for $type {
fn register(registry: &mut UserDataRegistry<Self>) {
let mut orig_registry = UserDataRegistry::new();
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
T::register(&mut orig_registry);
// Copy all fields, methods, etc. from the original registry
@@ -588,4 +806,19 @@ macro_rules! lua_userdata_impl {
// A special proxy object for UserData
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
lua_userdata_impl!(UserDataProxy<T>);
lua_userdata_impl!(UserDataProxy<T>, UserDataTypeId::Shared(TypeId::of::<T>()));
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
lua_userdata_impl!(Rc<T> => Rc);
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
lua_userdata_impl!(Rc<RefCell<T>> => RcRefCell);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<T> => Arc);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<Mutex<T>> => ArcMutex);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
+31
View File
@@ -0,0 +1,31 @@
use std::cell::Cell;
use std::marker::PhantomData;
// This is a trick to check if a type is `Sync` or not.
// It uses leaked specialization feature from stdlib.
struct IsSync<'a, T> {
is_sync: &'a Cell<bool>,
_marker: PhantomData<T>,
}
impl<T> Clone for IsSync<'_, T> {
fn clone(&self) -> Self {
self.is_sync.set(false);
IsSync {
is_sync: self.is_sync,
_marker: PhantomData,
}
}
}
impl<T: Sync> Copy for IsSync<'_, T> {}
pub(crate) fn is_sync<T>() -> bool {
let is_sync = Cell::new(true);
let _ = [IsSync::<T> {
is_sync: &is_sync,
_marker: PhantomData,
}]
.clone();
is_sync.get()
}
+7 -6
View File
@@ -204,24 +204,25 @@ pub(crate) unsafe fn protect_lua_closure<F, R>(
f: F,
) -> Result<R>
where
F: Fn(*mut ffi::lua_State) -> R,
F: FnOnce(*mut ffi::lua_State) -> R,
R: Copy,
{
struct Params<F, R: Copy> {
function: F,
function: Option<F>,
result: MaybeUninit<R>,
nresults: c_int,
}
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where
F: Fn(*mut ffi::lua_State) -> R,
F: FnOnce(*mut ffi::lua_State) -> R,
R: Copy,
{
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1);
(*params).result.write(((*params).function)(state));
let f = (*params).function.take().unwrap();
(*params).result.write(f(state));
if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state)
@@ -241,7 +242,7 @@ where
}
let mut params = Params {
function: f,
function: Some(f),
result: MaybeUninit::uninit(),
nresults,
};
@@ -365,7 +366,7 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
// Create destructed userdata metatable
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| Err(Error::CallbackDestructed))
callback_error(state, |_| Err(Error::UserDataDestructed))
}
push_table(state, 0, 26, true)?;
+37 -10
View File
@@ -1,6 +1,6 @@
use std::borrow::Cow;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int};
use std::os::raw::{c_char, c_int, c_void};
use std::{ptr, slice, str};
use crate::error::{Error, Result};
@@ -17,13 +17,9 @@ pub(crate) use userdata::{
DESTRUCTED_USERDATA_METATABLE,
};
#[cfg(not(feature = "lua54"))]
pub(crate) use userdata::push_userdata;
#[cfg(feature = "lua54")]
pub(crate) use userdata::push_userdata_uv;
#[cfg(not(feature = "luau"))]
pub(crate) use userdata::userdata_destructor;
pub(crate) use userdata::push_uninit_userdata;
pub(crate) use userdata::push_userdata;
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message.
@@ -67,9 +63,15 @@ impl StackGuard {
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
StackGuard { state, top }
}
#[inline]
pub(crate) fn keep(&mut self, n: c_int) {
self.top += n;
}
}
impl Drop for StackGuard {
#[track_caller]
fn drop(&mut self) {
unsafe {
let top = ffi::lua_gettop(self.state);
@@ -129,6 +131,15 @@ pub(crate) unsafe fn push_table(
}
}
// Uses 4 stack spaces, does not call checkstack.
pub(crate) unsafe fn rawget_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<c_int> {
ffi::lua_pushvalue(state, table);
protect_lua!(state, 1, 1, |state| {
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
ffi::lua_rawget(state, -2)
})
}
// Uses 4 stack spaces, does not call checkstack.
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
ffi::lua_pushvalue(state, table);
@@ -275,9 +286,25 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => format!("<cdata {:?}>", ffi::lua_topointer(state, index)),
_ => "<unknown>".to_string(),
type_id => {
let type_name = CStr::from_ptr(ffi::lua_typename(state, type_id)).to_string_lossy();
format!("<{type_name} {:?}>", ffi::lua_topointer(state, index))
}
}
}
#[inline(always)]
pub(crate) unsafe fn get_metatable_ptr(state: *mut ffi::lua_State, index: c_int) -> *const c_void {
#[cfg(feature = "luau")]
return ffi::lua_getmetatablepointer(state, index);
#[cfg(not(feature = "luau"))]
if ffi::lua_getmetatable(state, index) == 0 {
ptr::null()
} else {
let p = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
p
}
}
+2 -2
View File
@@ -21,7 +21,7 @@ impl TypeKey for String {
static CALLBACK_TYPE_KEY: u8 = 0;
impl TypeKey for Callback<'static> {
impl TypeKey for Callback {
#[inline(always)]
fn type_key() -> *const c_void {
&CALLBACK_TYPE_KEY as *const u8 as *const c_void
@@ -41,7 +41,7 @@ impl TypeKey for CallbackUpvalue {
static ASYNC_CALLBACK_TYPE_KEY: u8 = 0;
#[cfg(feature = "async")]
impl TypeKey for AsyncCallback<'static> {
impl TypeKey for AsyncCallback {
#[inline(always)]
fn type_key() -> *const c_void {
&ASYNC_CALLBACK_TYPE_KEY as *const u8 as *const c_void
+35 -64
View File
@@ -3,7 +3,7 @@ use std::os::raw::{c_int, c_void};
use std::{ptr, str};
use crate::error::Result;
use crate::util::{check_stack, push_string, push_table, rawset_field, TypeKey};
use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, rawset_field, TypeKey};
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack.
@@ -58,71 +58,54 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
state: *mut ffi::lua_State,
index: c_int,
type_mt_ptr: *const c_void,
mut type_mt_ptr: *const c_void,
) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
if ud.is_null() {
return ptr::null_mut();
}
if !type_mt_ptr.is_null() {
let ud_mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
if ud_mt_ptr != type_mt_ptr {
return ptr::null_mut();
}
} else {
let mt_ptr = get_metatable_ptr(state, index);
if type_mt_ptr.is_null() {
get_internal_metatable::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if res == 0 {
return ptr::null_mut();
}
type_mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
}
if mt_ptr != type_mt_ptr {
return ptr::null_mut();
}
ud
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
#[cfg(not(feature = "luau"))]
let ud = if protect {
#[cfg(not(feature = "luau"))]
pub(crate) unsafe fn push_uninit_userdata<T>(state: *mut ffi::lua_State, protect: bool) -> Result<*mut T> {
if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
})?
})
} else {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
};
Ok(ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T)
}
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<*mut T> {
#[cfg(not(feature = "luau"))]
let ud_ptr = push_uninit_userdata(state, protect)?;
#[cfg(feature = "luau")]
let ud = if protect {
let ud_ptr = if protect {
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
} else {
ffi::lua_newuserdata_t::<T>(state)
};
ptr::write(ud, t);
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub(crate) unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
};
ptr::write(ud, t);
Ok(())
ptr::write(ud_ptr, t);
Ok(ud_ptr)
}
#[inline]
#[track_caller]
pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
@@ -169,16 +152,12 @@ pub(crate) unsafe fn init_userdata_metatable(
field_getters: Option<c_int>,
field_setters: Option<c_int>,
methods: Option<c_int>,
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
ffi::lua_pushvalue(state, metatable);
if field_getters.is_some() || methods.is_some() {
// Push `__index` generator function
init_userdata_metatable_index(state)?;
push_string(state, b"__index", true)?;
let index_type = ffi::lua_rawget(state, -3);
let index_type = rawget_field(state, metatable, "__index")?;
match index_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
for &idx in &[field_getters, methods] {
@@ -192,39 +171,31 @@ pub(crate) unsafe fn init_userdata_metatable(
// Generate `__index`
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
}
_ => mlua_panic!("improper __index type {}", index_type),
_ => mlua_panic!("improper `__index` type: {}", index_type),
}
rawset_field(state, -2, "__index")?;
rawset_field(state, metatable, "__index")?;
}
if let Some(field_setters) = field_setters {
// Push `__newindex` generator function
init_userdata_metatable_newindex(state)?;
push_string(state, b"__newindex", true)?;
let newindex_type = ffi::lua_rawget(state, -3);
let newindex_type = rawget_field(state, metatable, "__newindex")?;
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
ffi::lua_pushvalue(state, field_setters);
// Generate `__newindex`
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
}
_ => mlua_panic!("improper __newindex type {}", newindex_type),
_ => mlua_panic!("improper `__newindex` type: {}", newindex_type),
}
rawset_field(state, -2, "__newindex")?;
}
// Additional initialization
if let Some(extra_init) = extra_init {
extra_init(state)?;
rawset_field(state, metatable, "__newindex")?;
}
ffi::lua_pushboolean(state, 0);
rawset_field(state, -2, "__metatable")?;
ffi::lua_pop(state, 1);
rawset_field(state, metatable, "__metatable")?;
Ok(())
}
@@ -368,7 +339,7 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
}
#[cfg(not(feature = "luau"))]
pub(crate) unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
// It's probably NOT a good idea to catch Rust panics in finalizer
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
take_userdata::<T>(state);
+118 -317
View File
@@ -1,20 +1,17 @@
use std::cmp::Ordering;
use std::collections::{vec_deque, HashSet, VecDeque};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_int, c_void};
use std::collections::HashSet;
use std::os::raw::c_void;
use std::string::String as StdString;
use std::sync::Arc;
use std::{fmt, mem, ptr, str};
use std::{fmt, ptr, str};
use num_traits::FromPrimitive;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, RawLua};
use crate::string::{BorrowedStr, String};
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number, SubtypeId};
use crate::types::{Integer, LightUserData, Number, ValueRef};
use crate::userdata::AnyUserData;
use crate::util::{check_stack, StackGuard};
@@ -26,9 +23,11 @@ use {
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
};
/// 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.
/// A dynamically typed Lua value.
///
/// The non-primitive variants (eg. string/table/function/thread/userdata) 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(Clone)]
pub enum Value {
/// The Lua value `nil`.
@@ -46,7 +45,7 @@ pub enum Value {
/// A Luau vector.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Vector(crate::types::Vector),
Vector(crate::Vector),
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
@@ -58,10 +57,18 @@ pub enum Value {
/// Reference to a Lua thread (or coroutine).
Thread(Thread),
/// 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),
/// A Luau buffer.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Buffer(crate::Buffer),
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Box<Error>),
/// Any other value not known to mlua (eg. LuaJIT CData).
#[allow(private_interfaces)]
Other(ValueRef),
}
pub use self::Value::Nil;
@@ -73,7 +80,7 @@ impl Value {
pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
/// Returns type name of this value.
pub const fn type_name(&self) -> &'static str {
pub fn type_name(&self) -> &'static str {
match *self {
Value::Nil => "nil",
Value::Boolean(_) => "boolean",
@@ -86,27 +93,26 @@ impl Value {
Value::Table(_) => "table",
Value::Function(_) => "function",
Value::Thread(_) => "thread",
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
Value::UserData(_) => "userdata",
#[cfg(feature = "luau")]
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
#[cfg(feature = "luajit")]
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
Value::Buffer(_) => "buffer",
Value::Error(_) => "error",
Value::Other(_) => "other",
}
}
/// 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:
/// 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.
/// If table 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()) {
pub fn equals(&self, other: &Self) -> Result<bool> {
match (self, other) {
(Value::Table(a), Value::Table(b)) => a.equals(b),
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
(a, b) => Ok(a == b),
@@ -123,21 +129,42 @@ impl Value {
#[inline]
pub fn to_pointer(&self) -> *const c_void {
match self {
Value::String(String(vref)) => {
// In Lua < 5.4 (excluding Luau), string pointers are NULL
// Use alternative approach
let lua = vref.lua.lock();
unsafe { ffi::lua_tostring(lua.ref_thread(), vref.index) as *const c_void }
}
Value::LightUserData(ud) => ud.0,
Value::String(String(r))
| Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
Value::Table(Table(vref))
| Value::Function(Function(vref))
| Value::Thread(Thread(vref, ..))
| Value::UserData(AnyUserData(vref))
| Value::Other(vref) => vref.to_pointer(),
#[cfg(feature = "luau")]
Value::Buffer(crate::Buffer(vref)) => vref.to_pointer(),
_ => ptr::null(),
}
}
/// Converts the value to a string.
///
/// If the value has a metatable with a `__tostring` method, then it will be called to get the
/// result.
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
/// functions).
pub fn to_string(&self) -> Result<StdString> {
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
let lua = vref.lua.lock();
let state = lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_ref(vref);
protect_lua!(state, 1, 1, fn(state) {
ffi::luaL_tolstring(state, -1, ptr::null_mut());
})?;
Ok(String(lua.pop_ref()).to_str()?.to_string())
}
match self {
Value::Nil => Ok("nil".to_string()),
Value::Boolean(b) => Ok(b.to_string()),
@@ -148,21 +175,13 @@ impl Value {
#[cfg(feature = "luau")]
Value::Vector(v) => Ok(v.to_string()),
Value::String(s) => Ok(s.to_str()?.to_string()),
Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r, ..)) => unsafe {
let lua = r.lua.lock();
let state = lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_ref(r);
protect_lua!(state, 1, 1, fn(state) {
ffi::luaL_tolstring(state, -1, ptr::null_mut());
})?;
Ok(String(lua.pop_ref()).to_str()?.to_string())
},
Value::Table(Table(vref))
| Value::Function(Function(vref))
| Value::Thread(Thread(vref, ..))
| Value::UserData(AnyUserData(vref))
| Value::Other(vref) => unsafe { invoke_to_string(vref) },
#[cfg(feature = "luau")]
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_to_string(vref) },
Value::Error(err) => Ok(err.to_string()),
}
}
@@ -174,6 +193,8 @@ impl Value {
}
/// Returns `true` if the value is a [`NULL`].
///
/// [`NULL`]: Value::NULL
#[inline]
pub fn is_null(&self) -> bool {
self == &Self::NULL
@@ -414,26 +435,45 @@ impl Value {
}
}
/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
/// Cast the value to a [`Buffer`].
///
/// If the value is [`Buffer`], returns it or `None` otherwise.
///
/// [`Buffer`]: crate::Buffer
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
#[inline]
pub fn is_buffer(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == SubtypeId::Buffer)
.unwrap_or_default()
pub fn as_buffer(&self) -> Option<&crate::Buffer> {
match self {
Value::Buffer(b) => Some(b),
_ => None,
}
}
/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
#[doc(hidden)]
/// Returns `true` if the value is a [`Buffer`].
///
/// [`Buffer`]: crate::Buffer
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[inline]
pub fn is_cdata(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == SubtypeId::CData)
.unwrap_or_default()
pub fn is_buffer(&self) -> bool {
self.as_buffer().is_some()
}
/// Returns `true` if the value is an [`Error`].
#[inline]
pub fn is_error(&self) -> bool {
self.as_error().is_some()
}
/// Cast the value to [`Error`].
///
/// If the value is an [`Error`], returns it or `None` otherwise.
pub fn as_error(&self) -> Option<&Error> {
match self {
Value::Error(e) => Some(e),
_ => None,
}
}
/// Wrap reference to this Value into [`SerializableValue`].
@@ -448,7 +488,7 @@ impl Value {
// Compares two values.
// Used to sort values for Debug printing.
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
pub(crate) fn sort_cmp(&self, other: &Self) -> Ordering {
fn cmp_num(a: Number, b: Number) -> Ordering {
match (a, b) {
_ if a < b => Ordering::Less,
@@ -472,16 +512,19 @@ impl Value {
(_, Value::Boolean(_)) => Ordering::Greater,
// Integer && Number
(Value::Integer(a), Value::Integer(b)) => a.cmp(b),
(&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
(&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
(Value::Integer(a), Value::Number(b)) => cmp_num(*a as Number, *b),
(Value::Number(a), Value::Integer(b)) => cmp_num(*a, *b as Number),
(Value::Number(a), Value::Number(b)) => cmp_num(*a, *b),
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
// Vector (Luau)
#[cfg(feature = "luau")]
(Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
// String
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
// Other variants can be randomly ordered
// Other variants can be ordered by their pointer
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
}
}
@@ -518,8 +561,11 @@ impl Value {
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
write!(fmt, "{s}")
}
#[cfg(feature = "luau")]
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
Value::Error(_) => write!(fmt, "error"),
Value::Other(v) => write!(fmt, "other: {:?}", v.to_pointer()),
}
}
}
@@ -529,6 +575,7 @@ impl fmt::Debug for Value {
if fmt.alternate() {
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
}
match self {
Value::Nil => write!(fmt, "Nil"),
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
@@ -542,7 +589,10 @@ impl fmt::Debug for Value {
Value::Function(f) => write!(fmt, "{f:?}"),
Value::Thread(t) => write!(fmt, "{t:?}"),
Value::UserData(ud) => write!(fmt, "{ud:?}"),
#[cfg(feature = "luau")]
Value::Buffer(buf) => write!(fmt, "{buf:?}"),
Value::Error(e) => write!(fmt, "Error({e:?})"),
Value::Other(v) => write!(fmt, "Other({v:?})"),
}
}
}
@@ -564,18 +614,13 @@ impl PartialEq for Value {
(Value::Function(a), Value::Function(b)) => a == b,
(Value::Thread(a), Value::Thread(b)) => a == b,
(Value::UserData(a), Value::UserData(b)) => a == b,
#[cfg(feature = "luau")]
(Value::Buffer(a), Value::Buffer(b)) => a == b,
_ => false,
}
}
}
impl AsRef<Value> for Value {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
/// A wrapped [`Value`] with customized serialization behavior.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
@@ -650,7 +695,7 @@ impl<'a> SerializableValue<'a> {
}
#[cfg(feature = "serialize")]
impl<'a> Serialize for SerializableValue<'a> {
impl Serialize for SerializableValue<'_> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
@@ -672,11 +717,14 @@ impl<'a> Serialize for SerializableValue<'a> {
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
ud.serialize(serializer)
}
#[cfg(feature = "luau")]
Value::Buffer(buf) => buf.serialize(serializer),
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
| Value::Error(_)
| Value::Other(_) => {
if self.options.deny_unsupported_types {
let msg = format!("cannot serialize <{}>", self.value.type_name());
Err(ser::Error::custom(msg))
@@ -688,259 +736,12 @@ impl<'a> Serialize for SerializableValue<'a> {
}
}
/// Trait for types convertible to `Value`.
pub trait IntoLua: Sized {
/// Performs the conversion.
fn into_lua(self, lua: &Lua) -> Result<Value>;
/// Pushes the value into the Lua stack.
///
/// # Safety
/// This method does not check Lua stack space.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_value(&self.into_lua(lua.lua())?)
}
}
/// Trait for types convertible from `Value`.
pub trait FromLua: Sized {
/// Performs the conversion.
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
/// `i` is the argument index (position),
/// `to` is a function name that received the argument.
#[doc(hidden)]
#[inline]
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
/// Performs the conversion for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx), lua.lua())
}
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Debug, Clone)]
pub struct MultiValue {
deque: VecDeque<Value>,
// FIXME
// lua: Option<&'static Lua>,
}
impl Drop for MultiValue {
fn drop(&mut self) {
// FIXME
// if let Some(lua) = self.lua {
// let vec = mem::take(&mut self.deque);
// lua.push_multivalue_to_pool(vec);
// }
}
}
impl Default for MultiValue {
#[inline]
fn default() -> MultiValue {
MultiValue::new()
}
}
impl Deref for MultiValue {
type Target = VecDeque<Value>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.deque
}
}
impl DerefMut for MultiValue {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.deque
}
}
impl MultiValue {
/// Creates an empty `MultiValue` containing no values.
pub const fn new() -> MultiValue {
MultiValue {
deque: VecDeque::new(),
// lua: None,
}
}
/// Similar to `new` but can reuse previously used container with allocated capacity.
#[inline]
pub(crate) fn with_lua_and_capacity(_lua: &Lua, capacity: usize) -> MultiValue {
// FIXME
// let deque = lua
// .pop_multivalue_from_pool()
// .map(|mut deque| {
// if capacity > 0 {
// deque.reserve(capacity);
// }
// deque
// })
// .unwrap_or_else(|| VecDeque::with_capacity(capacity));
let deque = VecDeque::with_capacity(capacity);
MultiValue {
deque,
// lua: Some(lua),
}
}
#[inline]
pub(crate) fn extend_from_values(&mut self, iter: impl IntoIterator<Item = Result<Value>>) -> Result<()> {
for value in iter {
self.push_back(value?);
}
Ok(())
}
}
impl FromIterator<Value> for MultiValue {
#[inline]
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let deque = VecDeque::from_iter(iter);
MultiValue {
deque,
// lua: None,
}
}
}
impl IntoIterator for MultiValue {
type Item = Value;
type IntoIter = vec_deque::IntoIter<Value>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let deque = mem::take(&mut self.deque);
mem::forget(self);
deque.into_iter()
}
}
impl<'a> IntoIterator for &'a MultiValue {
type Item = &'a Value;
type IntoIter = vec_deque::Iter<'a, Value>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.deque.iter()
}
}
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
pub trait IntoLuaMulti: Sized {
/// Performs the conversion.
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
/// Pushes the values into the Lua stack.
///
/// Returns number of pushed values.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let values = self.into_lua_multi(lua.lua())?;
let len: c_int = values.len().try_into().unwrap();
unsafe {
check_stack(lua.state(), len + 1)?;
for val in &values {
lua.push_value(val)?;
}
}
Ok(len)
}
}
/// Trait for types that can be created from an arbitrary number of Lua values.
///
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
pub trait FromLuaMulti: Sized {
/// Performs the conversion.
///
/// In case `values` contains more values than needed to perform the conversion, the excess
/// values should be ignored. This reflects the semantics of Lua when calling a function or
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
/// Performs the conversion for a list of arguments.
///
/// `i` is an index (position) of the first argument,
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(args, lua)
}
/// Performs the conversion for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
let mut values = MultiValue::with_lua_and_capacity(lua.lua(), nvals as usize);
for idx in 0..nvals {
values.push_back(lua.stack_value(-nvals + idx));
}
if nvals > 0 {
// It's safe to clear the stack as all references moved to ref thread
ffi::lua_pop(lua.state(), nvals);
}
Self::from_lua_multi(values, lua.lua())
}
/// Same as `from_lua_args` but for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
let _ = (i, to);
Self::from_stack_multi(nargs, lua)
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Value: Send);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(MultiValue: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Value: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
}
+92
View File
@@ -0,0 +1,92 @@
use std::fmt;
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
/// A Luau vector type.
///
/// By default vectors are 3-dimensional, but can be 4-dimensional
/// if the `luau-vector4` feature is enabled.
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
impl fmt::Display for Vector {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "luau-vector4"))]
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
#[cfg(feature = "luau-vector4")]
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
}
}
#[cfg_attr(not(feature = "luau"), allow(unused))]
impl Vector {
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
/// Creates a new vector.
#[cfg(not(feature = "luau-vector4"))]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self([x, y, z])
}
/// Creates a new vector.
#[cfg(feature = "luau-vector4")]
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self([x, y, z, w])
}
/// Creates a new vector with all components set to `0.0`.
#[doc(hidden)]
pub const fn zero() -> Self {
Self([0.0; Self::SIZE])
}
/// Returns 1st component of the vector.
pub const fn x(&self) -> f32 {
self.0[0]
}
/// Returns 2nd component of the vector.
pub const fn y(&self) -> f32 {
self.0[1]
}
/// Returns 3rd component of the vector.
pub const fn z(&self) -> f32 {
self.0[2]
}
/// Returns 4th component of the vector.
#[cfg(any(feature = "luau-vector4", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
pub const fn w(&self) -> f32 {
self.0[3]
}
}
#[cfg(feature = "serialize")]
impl Serialize for Vector {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
ts.serialize_field(&self.x())?;
ts.serialize_field(&self.y())?;
ts.serialize_field(&self.z())?;
#[cfg(feature = "luau-vector4")]
ts.serialize_field(&self.w())?;
ts.end()
}
}
impl PartialEq<[f32; Self::SIZE]> for Vector {
#[inline]
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
self.0 == *other
}
}
#[cfg(feature = "luau")]
impl crate::types::LuaType for Vector {
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TVECTOR;
}
+21 -6
View File
@@ -1,8 +1,23 @@
[lua54_coverage]
features = "lua54,vendored,async,serialize,macros,unstable"
[lua54]
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
[lua51_coverage]
features = "lua51,vendored,async,serialize,macros,unstable"
[lua54_non_send]
features = "lua54,vendored,async,serialize,macros,anyhow,userdata-wrappers"
[luau_coverage]
features = "luau,async,serialize,macros,unstable"
[lua54_with_memory_limit]
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
rustflags = "--cfg force_memory_limit"
[lua51]
features = "lua51,vendored,async,send,serialize,macros"
[lua51_with_memory_limit]
features = "lua51,vendored,async,send,serialize,macros"
rustflags = "--cfg force_memory_limit"
[luau]
features = "luau,async,send,serialize,macros"
[luau_with_memory_limit]
features = "luau,async,send,serialize,macros"
rustflags = "--cfg force_memory_limit"
+105 -54
View File
@@ -1,12 +1,14 @@
#![cfg(feature = "async")]
use std::sync::{Arc, Mutex};
use std::string::String as StdString;
use std::sync::Arc;
use std::time::Duration;
use futures_util::stream::TryStreamExt;
use tokio::sync::Mutex;
use mlua::{
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt, UserData,
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
UserDataMethods, Value,
};
@@ -38,12 +40,51 @@ async fn test_async_function() -> Result<()> {
async fn test_async_function_wrap() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_async(|_, s: String| async move { Ok(s) });
let f = Function::wrap_async(|s: StdString| async move {
tokio::task::yield_now().await;
Ok(s)
});
lua.globals().set("f", f)?;
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
assert_eq!(res, "hello");
// Return error
let ferr = Function::wrap_async(|| async move { Err::<(), _>(Error::runtime("some async error")) });
lua.globals().set("ferr", ferr)?;
lua.load(
r#"
local ok, err = pcall(ferr)
assert(not ok and tostring(err):find("some async error"))
"#,
)
.exec_async()
.await
.unwrap();
Ok(())
}
#[tokio::test]
async fn test_async_function_wrap_raw() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_raw_async(|s: StdString| async move {
tokio::task::yield_now().await;
s
});
lua.globals().set("f", f)?;
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
assert_eq!(res, "hello");
// Return error
let ferr = Function::wrap_raw_async(|| async move {
tokio::task::yield_now().await;
Err::<(), _>("some error")
});
lua.globals().set("ferr", ferr)?;
let (_, err): (Value, String) = lua.load(r#"ferr()"#).eval_async().await?;
assert_eq!(err, "some error");
Ok(())
}
@@ -72,16 +113,16 @@ async fn test_async_call() -> Result<()> {
Ok(format!("hello, {}!", name))
})?;
match hello.call::<_, ()>("alex") {
match hello.call::<()>("alex") {
Err(Error::RuntimeError(_)) => {}
_ => panic!("non-async executing async function must fail on the yield stage with RuntimeError"),
err => panic!("expected `RuntimeError`, got {err:?}"),
};
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
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);
assert_eq!(sum.call_async::<i64>((5, 1)).await?, 6);
Ok(())
}
@@ -95,7 +136,7 @@ async fn test_async_call_many_returns() -> Result<()> {
Ok(("a", "b", "c", 1))
})?;
let vals = hello.call_async::<_, MultiValue>(()).await?;
let vals = hello.call_async::<MultiValue>(()).await?;
assert_eq!(vals.len(), 4);
assert_eq!(vals[0].to_string()?, "a");
assert_eq!(vals[1].to_string()?, "b");
@@ -158,7 +199,7 @@ async fn test_async_handle_yield() -> Result<()> {
"#,
)
.eval::<Function>()?;
assert_eq!(min.call_async::<_, i64>((-1, 1)).await?, -1);
assert_eq!(min.call_async::<i64>((-1, 1)).await?, -1);
Ok(())
}
@@ -227,15 +268,15 @@ async fn test_async_lua54_to_be_closed() -> Result<()> {
let f = lua.load(code).into_function()?;
// Test close using call_async
let _ = f.call_async::<_, ()>(()).await;
assert_eq!(globals.get::<_, usize>("close_count")?, 1);
let _ = f.call_async::<()>(()).await;
assert_eq!(globals.get::<usize>("close_count")?, 1);
// Don't close by default when awaiting async threads
let co = lua.create_thread(f.clone())?;
let _ = co.clone().into_async::<_, ()>(()).await;
assert_eq!(globals.get::<_, usize>("close_count")?, 1);
let _ = co.clone().into_async::<()>(()).await;
assert_eq!(globals.get::<usize>("close_count")?, 1);
let _ = co.reset(f);
assert_eq!(globals.get::<_, usize>("close_count")?, 2);
assert_eq!(globals.get::<usize>("close_count")?, 2);
Ok(())
}
@@ -259,7 +300,7 @@ async fn test_async_thread_stream() -> Result<()> {
.eval()?,
)?;
let mut stream = thread.into_async::<_, i64>(1);
let mut stream = thread.into_async::<i64>(1);
let mut sum = 0;
while let Some(n) = stream.try_next().await? {
sum += n;
@@ -307,14 +348,14 @@ fn test_async_thread_capture() -> Result<()> {
let thread = lua.create_thread(f)?;
// After first resume, `v: Value` is captured in the coroutine
thread.resume::<_, ()>("abc").unwrap();
thread.resume::<()>("abc").unwrap();
drop(thread);
Ok(())
}
#[tokio::test]
async fn test_async_table() -> Result<()> {
async fn test_async_table_object_like() -> Result<()> {
let options = LuaOptions::new().thread_pool_size(4);
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
@@ -323,7 +364,7 @@ async fn test_async_table() -> Result<()> {
let get_value = lua.create_async_function(|_, table: Table| async move {
sleep_ms(10).await;
table.get::<_, i64>("val")
table.get::<i64>("val")
})?;
table.set("get_value", get_value)?;
@@ -333,19 +374,27 @@ async fn test_async_table() -> Result<()> {
})?;
table.set("set_value", set_value)?;
let sleep = lua.create_async_function(|_, n| async move {
sleep_ms(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_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"
);
let metatable = lua.create_table()?;
metatable.set(
"__call",
lua.create_async_function(|_, table: Table| async move {
sleep_ms(10).await;
table.get::<i64>("val")
})?,
)?;
table.set_metatable(Some(metatable));
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
match table.call_async_method::<()>("non_existent", ()).await {
Err(Error::RuntimeError(err)) => {
assert!(err.contains("attempt to call a nil value (function 'non_existent')"))
}
r => panic!("expected RuntimeError, got {r:?}"),
}
Ok(())
}
@@ -365,9 +414,9 @@ async fn test_async_thread_pool() -> Result<()> {
Ok(format!("elapsed:{}ms", n))
})?;
assert!(error_f.call_async::<_, ()>(()).await.is_err());
assert!(error_f.call_async::<()>(()).await.is_err());
// Next call should use cached thread
assert_eq!(sleep.call_async::<_, String>(3).await?, "elapsed:3ms");
assert_eq!(sleep.call_async::<String>(3).await?, "elapsed:3ms");
Ok(())
}
@@ -377,13 +426,13 @@ async fn test_async_userdata() -> Result<()> {
struct MyUserData(u64);
impl UserData for MyUserData {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
sleep_ms(10).await;
Ok(data.0)
});
methods.add_async_method_mut("set_value", |_, data, n| async move {
methods.add_async_method_mut("set_value", |_, mut data, n| async move {
sleep_ms(10).await;
data.0 = n;
Ok(())
@@ -414,7 +463,7 @@ async fn test_async_userdata() -> Result<()> {
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method_mut(
mlua::MetaMethod::NewIndex,
|_, data, (key, value): (String, f64)| async move {
|_, mut data, (key, value): (String, f64)| async move {
sleep_ms(10).await;
match key.as_str() {
"ms" => data.0 = value as u64,
@@ -460,13 +509,14 @@ async fn test_async_userdata() -> Result<()> {
.exec_async()
.await?;
userdata.call_async_method("set_value", 24).await?;
// ObjectLike methods
userdata.call_async_method::<()>("set_value", 24).await?;
let n: u64 = userdata.call_async_method("get_value", ()).await?;
assert_eq!(n, 24);
userdata.call_async_function("sleep", 15).await?;
userdata.call_async_function::<()>("sleep", 15).await?;
#[cfg(not(any(feature = "lua51", feature = "luau")))]
assert_eq!(userdata.call_async::<_, String>(()).await?, "elapsed:24ms");
assert_eq!(userdata.call_async::<String>(()).await?, "elapsed:24ms");
Ok(())
}
@@ -476,7 +526,7 @@ async fn test_async_thread_error() -> Result<()> {
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method("__tostring", |_, _this, ()| Ok("myuserdata error"))
}
}
@@ -485,7 +535,7 @@ async fn test_async_thread_error() -> Result<()> {
let result = lua
.load("function x(...) error(...) end x(...)")
.set_name("chunk")
.call_async::<_, ()>(MyUserData)
.call_async::<()>(MyUserData)
.await;
assert!(
matches!(result, Err(Error::RuntimeError(cause)) if cause.contains("myuserdata error")),
@@ -497,21 +547,22 @@ async fn test_async_thread_error() -> Result<()> {
#[tokio::test]
async fn test_async_terminate() -> Result<()> {
let lua = Lua::new();
let mutex = Arc::new(Mutex::new(0u32));
let mutex2 = mutex.clone();
let func = lua.create_async_function(move |_, ()| {
let mutex = mutex2.clone();
async move {
let _guard = mutex.lock();
sleep_ms(100).await;
Ok(())
}
})?;
{
let lua = Lua::new();
let mutex2 = mutex.clone();
let func = lua.create_async_function(move |lua, ()| {
let mutex = mutex2.clone();
async move {
let _guard = mutex.lock().await;
sleep_ms(100).await;
drop(lua); // Move Lua to the future to test drop
Ok(())
}
})?;
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<_, ()>(())).await;
lua.gc_collect()?;
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<()>(())).await;
}
assert!(mutex.try_lock().is_ok());
Ok(())
+57
View File
@@ -0,0 +1,57 @@
#![cfg(feature = "luau")]
use mlua::{Lua, Result, Value};
#[test]
fn test_buffer() -> Result<()> {
let lua = Lua::new();
let buf1 = lua
.load(
r#"
local buf = buffer.fromstring("hello")
assert(buffer.len(buf) == 5)
return buf
"#,
)
.eval::<Value>()?;
assert!(buf1.is_buffer());
assert_eq!(buf1.type_name(), "buffer");
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
assert_ne!(buf1, buf2);
// Check that we can pass buffer type to Lua
let buf1 = buf1.as_buffer().unwrap();
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
assert!(func.call::<String>(buf1)?.starts_with("buffer:"));
// Check buffer methods
assert_eq!(buf1.len(), 5);
assert_eq!(buf1.to_vec(), b"hello");
assert_eq!(buf1.read_bytes::<3>(1), [b'e', b'l', b'l']);
buf1.write_bytes(1, b"i");
assert_eq!(buf1.to_vec(), b"hillo");
let buf3 = lua.create_buffer(b"")?;
assert!(buf3.is_empty());
assert!(!Value::Buffer(buf3).to_pointer().is_null());
Ok(())
}
#[test]
#[should_panic(expected = "range end index 14 out of range for slice of length 13")]
fn test_buffer_out_of_bounds_read() {
let lua = Lua::new();
let buf = lua.create_buffer(b"hello, world!").unwrap();
_ = buf.read_bytes::<1>(13);
}
#[test]
#[should_panic(expected = "range end index 16 out of range for slice of length 13")]
fn test_buffer_out_of_bounds_write() {
let lua = Lua::new();
let buf = lua.create_buffer(b"hello, world!").unwrap();
buf.write_bytes(14, b"!!");
}
+24 -24
View File
@@ -22,38 +22,38 @@ fn test_byte_string_round_trip() -> Result<()> {
let globals = lua.globals();
let isi = globals.get::<_, BString>("invalid_sequence_identifier")?;
let isi = globals.get::<BString>("invalid_sequence_identifier")?;
assert_eq!(isi, [0xa0, 0xa1].as_ref());
let i2os2 = globals.get::<_, BString>("invalid_2_octet_sequence_2nd")?;
let i2os2 = globals.get::<BString>("invalid_2_octet_sequence_2nd")?;
assert_eq!(i2os2, [0xc3, 0x28].as_ref());
let i3os2 = globals.get::<_, BString>("invalid_3_octet_sequence_2nd")?;
let i3os2 = globals.get::<BString>("invalid_3_octet_sequence_2nd")?;
assert_eq!(i3os2, [0xe2, 0x28, 0xa1].as_ref());
let i3os3 = globals.get::<_, BString>("invalid_3_octet_sequence_3rd")?;
let i3os3 = globals.get::<BString>("invalid_3_octet_sequence_3rd")?;
assert_eq!(i3os3, [0xe2, 0x82, 0x28].as_ref());
let i4os2 = globals.get::<_, BString>("invalid_4_octet_sequence_2nd")?;
let i4os2 = globals.get::<BString>("invalid_4_octet_sequence_2nd")?;
assert_eq!(i4os2, [0xf0, 0x28, 0x8c, 0xbc].as_ref());
let i4os3 = globals.get::<_, BString>("invalid_4_octet_sequence_3rd")?;
let i4os3 = globals.get::<BString>("invalid_4_octet_sequence_3rd")?;
assert_eq!(i4os3, [0xf0, 0x90, 0x28, 0xbc].as_ref());
let i4os4 = globals.get::<_, BString>("invalid_4_octet_sequence_4th")?;
let i4os4 = globals.get::<BString>("invalid_4_octet_sequence_4th")?;
assert_eq!(i4os4, [0xf0, 0x28, 0x8c, 0x28].as_ref());
let aas = globals.get::<_, BString>("an_actual_string")?;
let aas = globals.get::<BString>("an_actual_string")?;
assert_eq!(aas, b"Hello, world!".as_ref());
globals.set::<_, &BStr>("bstr_invalid_sequence_identifier", isi.as_ref())?;
globals.set::<_, &BStr>("bstr_invalid_2_octet_sequence_2nd", i2os2.as_ref())?;
globals.set::<_, &BStr>("bstr_invalid_3_octet_sequence_2nd", i3os2.as_ref())?;
globals.set::<_, &BStr>("bstr_invalid_3_octet_sequence_3rd", i3os3.as_ref())?;
globals.set::<_, &BStr>("bstr_invalid_4_octet_sequence_2nd", i4os2.as_ref())?;
globals.set::<_, &BStr>("bstr_invalid_4_octet_sequence_3rd", i4os3.as_ref())?;
globals.set::<_, &BStr>("bstr_invalid_4_octet_sequence_4th", i4os4.as_ref())?;
globals.set::<_, &BStr>("bstr_an_actual_string", aas.as_ref())?;
globals.set("bstr_invalid_sequence_identifier", isi.as_ref() as &BStr)?;
globals.set("bstr_invalid_2_octet_sequence_2nd", i2os2.as_ref() as &BStr)?;
globals.set("bstr_invalid_3_octet_sequence_2nd", i3os2.as_ref() as &BStr)?;
globals.set("bstr_invalid_3_octet_sequence_3rd", i3os3.as_ref() as &BStr)?;
globals.set("bstr_invalid_4_octet_sequence_2nd", i4os2.as_ref() as &BStr)?;
globals.set("bstr_invalid_4_octet_sequence_3rd", i4os3.as_ref() as &BStr)?;
globals.set("bstr_invalid_4_octet_sequence_4th", i4os4.as_ref() as &BStr)?;
globals.set("bstr_an_actual_string", aas.as_ref() as &BStr)?;
lua.load(
r#"
@@ -69,14 +69,14 @@ fn test_byte_string_round_trip() -> Result<()> {
)
.exec()?;
globals.set::<_, BString>("bstring_invalid_sequence_identifier", isi)?;
globals.set::<_, BString>("bstring_invalid_2_octet_sequence_2nd", i2os2)?;
globals.set::<_, BString>("bstring_invalid_3_octet_sequence_2nd", i3os2)?;
globals.set::<_, BString>("bstring_invalid_3_octet_sequence_3rd", i3os3)?;
globals.set::<_, BString>("bstring_invalid_4_octet_sequence_2nd", i4os2)?;
globals.set::<_, BString>("bstring_invalid_4_octet_sequence_3rd", i4os3)?;
globals.set::<_, BString>("bstring_invalid_4_octet_sequence_4th", i4os4)?;
globals.set::<_, BString>("bstring_an_actual_string", aas)?;
globals.set("bstring_invalid_sequence_identifier", isi)?;
globals.set("bstring_invalid_2_octet_sequence_2nd", i2os2)?;
globals.set("bstring_invalid_3_octet_sequence_2nd", i3os2)?;
globals.set("bstring_invalid_3_octet_sequence_3rd", i3os3)?;
globals.set("bstring_invalid_4_octet_sequence_2nd", i4os2)?;
globals.set("bstring_invalid_4_octet_sequence_3rd", i4os3)?;
globals.set("bstring_invalid_4_octet_sequence_4th", i4os4)?;
globals.set("bstring_an_actual_string", aas)?;
lua.load(
r#"
+56 -3
View File
@@ -19,7 +19,7 @@ fn test_chunk_path() -> Result<()> {
return 321
"#,
)?;
let i: i32 = lua.load(&*temp_dir.path().join("module.lua")).eval()?;
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() {
@@ -27,6 +27,30 @@ fn test_chunk_path() -> Result<()> {
res => panic!("expected io::Error, got {:?}", res),
};
// &Path
assert_eq!(
(lua.load(&*temp_dir.path().join("module.lua").as_path())).eval::<i32>()?,
321
);
Ok(())
}
#[test]
fn test_chunk_impls() -> Result<()> {
let lua = Lua::new();
// StdString
assert_eq!(lua.load(String::from("1")).eval::<i32>()?, 1);
assert_eq!(lua.load(&String::from("2")).eval::<i32>()?, 2);
// &[u8]
assert_eq!(lua.load(&b"3"[..]).eval::<i32>()?, 3);
// Vec<u8>
assert_eq!(lua.load(b"4".to_vec()).eval::<i32>()?, 4);
assert_eq!(lua.load(&b"5".to_vec()).eval::<i32>()?, 5);
Ok(())
}
@@ -42,7 +66,7 @@ fn test_chunk_macro() -> Result<()> {
data.raw_set("num", 1)?;
let ud = mlua::AnyUserData::wrap("hello");
let f = mlua::Function::wrap(|_lua, ()| Ok(()));
let f = mlua::Function::wrap(|| Ok(()));
lua.globals().set("g", 123)?;
@@ -64,7 +88,36 @@ fn test_chunk_macro() -> Result<()> {
})
.exec()?;
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
assert_eq!(lua.globals().get::<i32>("s")?, 321);
Ok(())
}
#[cfg(feature = "luau")]
#[test]
fn test_compiler() -> Result<()> {
use std::vec;
let compiler = mlua::Compiler::new()
.set_optimization_level(2)
.set_debug_level(2)
.set_type_info_level(1)
.set_coverage_level(2)
.set_vector_lib("vector")
.set_vector_ctor("new")
.set_vector_type("vector")
.set_mutable_globals(vec!["mutable_global".into()])
.set_userdata_types(vec!["MyUserdata".into()]);
assert!(compiler.compile("return vector.new(1, 2, 3)").is_ok());
// Error
match compiler.compile("%") {
Err(mlua::Error::SyntaxError { ref message, .. }) => {
assert!(message.contains("Expected identifier when parsing expression, got '%'"),);
}
res => panic!("expected result: {res:?}"),
}
Ok(())
}
-4
View File
@@ -7,18 +7,14 @@ fn test_compilation() {
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_any_userdata_method.rs");
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
t.compile_fail("tests/compile/async_userdata_method.rs");
}
#[cfg(feature = "send")]
+5 -4
View File
@@ -1,4 +1,4 @@
use mlua::{UserDataMethods, Lua};
use mlua::{Lua, UserDataMethods};
fn main() {
let lua = Lua::new();
@@ -6,9 +6,10 @@ fn main() {
lua.register_userdata_type::<String>(|reg| {
let s = String::new();
let mut s = &s;
reg.add_async_method("t", |_, this: &String, ()| async {
s = this;
reg.add_async_method("t", |_, this, ()| async {
s = &*this;
Ok(())
});
}).unwrap();
})
.unwrap();
}
+46 -49
View File
@@ -1,29 +1,48 @@
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:58
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ___________________________________----------------------_^
| | | |
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10}` contains a lifetime `'2`
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^ cannot borrow as mutable
10 | s = &*this;
| - mutable borrow occurs due to use of `s` in closure
error[E0373]: async block may outlive the current function, but it borrows `this`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^ may outlive borrowed value `this`
10 | s = &*this;
| ---- `this` is borrowed here
|
note: async block is returned here
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| _________________________________________________^
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |_________^
help: to force the async block to take ownership of `this` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", |_, this, ()| async move {
| ++++
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| ___________________________________-------------_^
| | | |
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:49: 9:54}` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | s = this;
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
--> tests/compile/async_any_userdata_method.rs:9:58
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| __________________________________________________________^
10 | | s = this;
| | - mutable borrow occurs due to use of `s` in closure
11 | | Ok(())
12 | | });
| |_________^ cannot borrow as mutable
error[E0597]: `s` does not live long enough
--> tests/compile/async_any_userdata_method.rs:8:21
|
@@ -31,53 +50,31 @@ error[E0597]: `s` does not live long enough
| - binding `s` declared here
8 | let mut s = &s;
| ^^ borrowed value does not live long enough
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
9 | / reg.add_async_method("t", |_, this, ()| async {
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |__________- argument requires that `s` is borrowed for `'static`
13 | }).unwrap();
13 | })
| - `s` dropped here while still borrowed
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/async_any_userdata_method.rs:9:9
|
6 | lua.register_userdata_type::<String>(|reg| {
| ---
| |
| `reg` is a reference that is only valid in the closure body
| has type `&mut LuaUserDataRegistry<'1, std::string::String>`
...
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| | ^
| | |
| |__________`reg` escapes the closure body here
| argument requires that `'1` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistry<'_, std::string::String>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:35
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = this;
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = &*this;
| - `s` is borrowed here
|
note: function requires argument type to outlive `'static`
--> tests/compile/async_any_userdata_method.rs:9:9
|
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
9 | / reg.add_async_method("t", |_, this, ()| async {
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |__________^
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
9 | reg.add_async_method("t", move |_, this, ()| async {
| ++++
+2 -2
View File
@@ -4,8 +4,8 @@ fn main() {
#[derive(Clone)]
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
impl UserData for MyUserData<'_> {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("print", |_, data, ()| async move {
println!("{}", data.0);
Ok(())
@@ -1,11 +1,11 @@
error: lifetime may not live long enough
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
7 | impl<'a> UserData for MyUserData<'a> {
| -- lifetime `'a` defined here
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
7 | impl UserData for MyUserData<'_> {
| -- lifetime `'1` appears in the `impl`'s self type
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
9 | / methods.add_async_method("print", |_, data, ()| async move {
10 | | println!("{}", data.0);
11 | | Ok(())
12 | | });
| |______________^ requires that `'a` must outlive `'static`
| |______________^ requires that `'1` must outlive `'static`
-14
View File
@@ -1,14 +0,0 @@
use mlua::{UserData, UserDataMethods};
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this: &'static Self, ()| async {
Ok(())
});
// ^ lifetime may not live long enough
}
}
fn main() {}
@@ -1,17 +0,0 @@
warning: unused variable: `this`
--> tests/compile/async_userdata_method.rs:7:48
|
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> tests/compile/async_userdata_method.rs:7:9
|
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
8 | | Ok(())
9 | | });
| |__________^ argument requires that `'lua` must outlive `'static`
+1 -3
View File
@@ -6,7 +6,5 @@ fn main() {
let test = Test(0);
let lua = Lua::new();
let _ = lua.create_function(|_, ()| -> Result<i32> {
Ok(test.0)
});
let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
}
+14 -17
View File
@@ -1,20 +1,17 @@
error[E0373]: closure may outlive the current function, but it borrows `test.0`, which is owned by the current function
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test.0`
10 | Ok(test.0)
| ------ `test.0` is borrowed here
|
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
| ^^^^^^^^^^^^^^^^^^^^^^ ------ `test.0` is borrowed here
| |
| may outlive borrowed value `test.0`
|
note: function requires argument type to outlive `'static`
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| _____________^
10 | | Ok(test.0)
11 | | });
| |______^
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `test.0` (and any other referenced variables), use the `move` keyword
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
| ++++
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> { Ok(test.0) });
| ++++
+70 -29
View File
@@ -1,51 +1,36 @@
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `LuaInner`
--> src/lua.rs
|
| pub struct LuaInner {
| ^^^^^^^^
note: required because it appears within the type `ArcInner<LuaInner>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<LuaInner>`
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/lua.rs
--> src/state.rs
|
| pub struct Lua(Arc<LuaInner>);
| pub struct Lua {
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
@@ -53,7 +38,63 @@ note: required because it's used within this closure
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^
note: required by a bound in `catch_unwind`
note: required by a bound in `std::panic::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`
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `Cell<usize>`
--> $RUST/core/src/cell.rs
|
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct RawReentrantMutex<R, G> {
| ^^^^^^^^^^^^^^^^^
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/state.rs
|
| pub struct Lua {
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^
note: required by a bound in `std::panic::catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
+2 -4
View File
@@ -8,10 +8,8 @@ fn main() -> Result<()> {
let data = Rc::new(Cell::new(0));
lua.create_function(move |_, ()| {
Ok(data.get())
})?
.call::<_, i32>(())?;
lua.create_function(move |_, ()| Ok(data.get()))?
.call::<i32>(())?;
Ok(())
}
+14 -17
View File
@@ -1,28 +1,25 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| --------------- ^-----------
| | |
| _________|_______________within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
| | |
| | required by a bound introduced by this call
12 | | Ok(data.get())
13 | | })?
| |_____^ `Rc<Cell<i32>>` cannot be sent between threads safely
11 | lua.create_function(move |_, ()| Ok(data.get()))?
| --------------- ------------^^^^^^^^^^^^^^^
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
| required by a bound introduced by this call
|
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`, which is required by `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}: MaybeSend`
note: required because it's used within this closure
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
11 | lua.create_function(move |_, ()| Ok(data.get()))?
| ^^^^^^^^^^^^
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `mlua::types::MaybeSend`
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `MaybeSend`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
--> src/state.rs
|
| pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
| pub fn create_function<F, A, R>(&self, func: F) -> Result<Function>
| --------------- required by a bound in this associated function
...
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
| where
| F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+84 -43
View File
@@ -1,69 +1,110 @@
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `LuaInner`
--> src/lua.rs
| pub struct Weak<
| ^^^^
note: required because it appears within the type `mlua::state::WeakLua`
--> src/state.rs
|
| pub struct LuaInner {
| ^^^^^^^^
note: required because it appears within the type `ArcInner<LuaInner>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<LuaInner>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/lua.rs
|
| pub struct Lua(Arc<LuaInner>);
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it appears within the type `LuaRef<'_>`
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
| ^^^^^^^
note: required because it appears within the type `mlua::types::ValueRef`
--> src/types.rs
|
| pub(crate) struct LuaRef<'lua> {
| ^^^^^^
note: required because it appears within the type `Table<'_>`
| pub(crate) struct ValueRef {
| ^^^^^^^^
note: required because it appears within the type `LuaTable`
--> src/table.rs
|
| pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
| pub struct Table(pub(crate) ValueRef);
| ^^^^^
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
note: required by a bound in `catch_unwind`
note: required by a bound in `std::panic::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`
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `Cell<usize>`
--> $RUST/core/src/cell.rs
|
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct RawReentrantMutex<R, G> {
| ^^^^^^^^^^^^^^^^^
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Weak<
| ^^^^
note: required because it appears within the type `mlua::state::WeakLua`
--> src/state.rs
|
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
| ^^^^^^^
note: required because it appears within the type `mlua::types::ValueRef`
--> src/types.rs
|
| pub(crate) struct ValueRef {
| ^^^^^^^^
note: required because it appears within the type `LuaTable`
--> src/table.rs
|
| pub struct Table(pub(crate) ValueRef);
| ^^^^^
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
note: required by a bound in `std::panic::catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
+5 -9
View File
@@ -4,15 +4,11 @@ fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(move |_, t: Table| {
if let Some(old) = inner.take() {
// Access old callback `Lua`.
}
inner = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
let f = scope.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
+22 -24
View File
@@ -1,26 +1,24 @@
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
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
--> tests/compile/scope_callback_capture.rs:7:43
|
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
5 | lua.scope(|scope| {
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
6 | let mut inner: Option<Table> = None;
7 | let f = scope.create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
8 | inner = Some(t);
| ----- `inner` is borrowed here
|
note: function requires argument type to outlive `'1`
--> tests/compile/scope_callback_capture.rs:7:17
|
7 | let f = scope.create_function_mut(|_, t: Table| {
| _________________^
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
8 | | inner = Some(t);
9 | | Ok(())
10 | | })?;
| |__________^
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
7 | let f = scope.create_function_mut(move |_, t: Table| {
| ++++
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
Ok(())
});
}
-42
View File
@@ -1,42 +0,0 @@
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/scope_callback_inner.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(|_, t: Table| {
9 | | inner = Some(t);
10 | | Ok(())
11 | | })?;
| |______________^ `scope` escapes the closure body here
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
--> tests/compile/scope_callback_inner.rs:8:34
|
5 | lua.scope(|scope| {
| ----- has type `&mlua::Scope<'_, '2>`
...
8 | .create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
9 | inner = Some(t);
| ----- `inner` is borrowed here
|
note: function requires argument type to outlive `'2`
--> tests/compile/scope_callback_inner.rs:7:17
|
7 | let f = scope
| _________________^
8 | | .create_function_mut(|_, t: Table| {
9 | | inner = Some(t);
10 | | Ok(())
11 | | })?;
| |______________^
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
8 | .create_function_mut(move |_, t: Table| {
| ++++
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
let mut outer: Option<Table> = None;
lua.scope(|scope| {
let f = scope
.create_function_mut(|_, t: Table| {
outer = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
Ok(())
});
}
-30
View File
@@ -1,30 +0,0 @@
error[E0521]: borrowed data escapes outside of closure
--> $DIR/scope_callback_outer.rs:7:17
|
6 | lua.scope(|scope| {
| -----
| |
| `scope` declared here, outside of the closure body
| `scope` is a reference that is only valid in the closure body
7 | let f = scope
| _________________^
8 | | .create_function_mut(|_, t: Table| {
9 | | outer = Some(t);
10 | | Ok(())
11 | | })?;
| |______________^ `scope` escapes the closure body here
error[E0597]: `outer` does not live long enough
--> $DIR/scope_callback_outer.rs:9:17
|
6 | lua.scope(|scope| {
| ------- value captured here
...
9 | outer = Some(t);
| ^^^^^ borrowed value does not live long enough
...
15 | }
| -
| |
| `outer` dropped here while still borrowed
| borrow might be used here, when `outer` is dropped and runs the destructor for type `Option<LuaTable<'_>>`
+6 -7
View File
@@ -10,14 +10,13 @@ fn main() {
let f = {
let mut test = Test { field: 0 };
scope
.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
scope.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
};
f.call::<_, ()>(())
f.call::<()>(())
});
}
+14 -15
View File
@@ -1,25 +1,24 @@
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
--> tests/compile/scope_invariance.rs:14:38
--> tests/compile/scope_invariance.rs:13:39
|
9 | lua.scope(|scope| {
| ----- has type `&mlua::Scope<'_, '1>`
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
...
14 | .create_function_mut(|_, ()| {
| ^^^^^^^ may outlive borrowed value `test.field`
15 | test.field = 42;
| ---------- `test.field` is borrowed here
13 | scope.create_function_mut(|_, ()| {
| ^^^^^^^ may outlive borrowed value `test.field`
14 | test.field = 42;
| ---------- `test.field` is borrowed here
|
note: function requires argument type to outlive `'1`
--> tests/compile/scope_invariance.rs:13:13
|
13 | / scope
14 | | .create_function_mut(|_, ()| {
15 | | test.field = 42;
16 | | //~^ error: `test` does not live long enough
17 | | Ok(())
18 | | })?
| |__________________^
13 | / scope.create_function_mut(|_, ()| {
14 | | test.field = 42;
15 | | //~^ error: `test` does not live long enough
16 | | Ok(())
17 | | })?
| |______________^
help: to force the closure to take ownership of `test.field` (and any other referenced variables), use the `move` keyword
|
14 | .create_function_mut(move |_, ()| {
| ++++
13 | scope.create_function_mut(move |_, ()| {
| ++++
+3 -3
View File
@@ -2,14 +2,14 @@ use mlua::{Lua, UserData};
fn main() {
struct MyUserData<'a>(&'a mut i32);
impl<'a> UserData for MyUserData<'a> {}
impl UserData for MyUserData<'_> {}
let mut i = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
Ok(())
});
}
+10 -7
View File
@@ -1,9 +1,12 @@
error[E0499]: cannot borrow `i` as mutable more than once at a time
--> $DIR/scope_mutable_aliasing.rs:12:61
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
| ------ first mutable borrow occurs here
12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
| ------------------------- ^^^^^^ second mutable borrow occurs here
| |
| first borrow later used by call
10 | lua.scope(|scope| {
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
| -----------------------------------------
| | |
| | first mutable borrow occurs here
| argument requires that `i` is borrowed for `'1`
12 | let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
| ^^^^^^ second mutable borrow occurs here
+3 -3
View File
@@ -3,16 +3,16 @@ use mlua::{Lua, UserData};
fn main() {
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData<'a>(&'a i32);
impl<'a> UserData for MyUserData<'a> {}
impl UserData for MyUserData<'_> {}
let igood = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood)).unwrap();
let _ugood = scope.create_userdata(MyUserData(&igood)).unwrap();
let _ubad = {
let ibad = 42;
scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
scope.create_userdata(MyUserData(&ibad)).unwrap();
};
Ok(())
});
+6 -6
View File
@@ -1,15 +1,15 @@
error[E0597]: `ibad` does not live long enough
--> tests/compile/scope_userdata_borrow.rs:15:56
--> tests/compile/scope_userdata_borrow.rs:15:46
|
11 | lua.scope(|scope| {
| ----- has type `&mlua::Scope<'_, '1>`
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
...
14 | let ibad = 42;
| ---- binding `ibad` declared here
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
| -------------------------------------------^^^^^--
| | |
| | borrowed value does not live long enough
15 | scope.create_userdata(MyUserData(&ibad)).unwrap();
| ---------------------------------^^^^^--
| | |
| | borrowed value does not live long enough
| argument requires that `ibad` is borrowed for `'1`
16 | };
| - `ibad` dropped here while still borrowed
-32
View File
@@ -1,32 +0,0 @@
use std::cell::RefCell;
use mlua::{Lua, Result, Table};
fn main() -> Result<()> {
thread_local! {
static BAD_TIME: RefCell<Option<Table<'static>>> = RefCell::new(None);
}
let lua = Lua::new();
lua.create_function(|_, table: Table| {
BAD_TIME.with(|bt| {
*bt.borrow_mut() = Some(table);
});
Ok(())
})?
.call::<_, ()>(lua.create_table()?)?;
// In debug, this will panic with a reference leak before getting to the next part but
// it segfaults anyway.
drop(lua);
BAD_TIME.with(|bt| {
println!(
"you're gonna have a bad time: {}",
bt.borrow().as_ref().unwrap().len().unwrap()
);
});
Ok(())
}
-31
View File
@@ -1,31 +0,0 @@
error[E0597]: `lua` does not live long enough
--> tests/compile/static_callback_args.rs:12:5
|
10 | let lua = Lua::new();
| --- binding `lua` declared here
11 |
12 | lua.create_function(|_, table: Table| {
| ^^^ borrowed value does not live long enough
13 | / BAD_TIME.with(|bt| {
14 | | *bt.borrow_mut() = Some(table);
15 | | });
| |__________- argument requires that `lua` is borrowed for `'static`
...
32 | }
| - `lua` dropped here while still borrowed
error[E0505]: cannot move out of `lua` because it is borrowed
--> tests/compile/static_callback_args.rs:22:10
|
10 | let lua = Lua::new();
| --- binding `lua` declared here
11 |
12 | lua.create_function(|_, table: Table| {
| --- borrow of `lua` occurs here
13 | / BAD_TIME.with(|bt| {
14 | | *bt.borrow_mut() = Some(table);
15 | | });
| |__________- argument requires that `lua` is borrowed for `'static`
...
22 | drop(lua);
| ^^^ move out of `lua` occurs here
-19
View File
@@ -1,19 +0,0 @@
use mlua::{AnyUserData, Lua, Table, UserData, Result};
fn main() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData;
impl UserData for MyUserData {};
let _userdata_ref;
{
let touter = globals.get::<_, Table>("touter")?;
touter.set("userdata", lua.create_userdata(MyUserData)?)?;
let userdata = touter.get::<_, AnyUserData>("userdata")?;
_userdata_ref = userdata.borrow::<MyUserData>();
//~^ error: `userdata` does not live long enough
}
Ok(())
}
-13
View File
@@ -1,13 +0,0 @@
error[E0597]: `userdata` does not live long enough
--> $DIR/userdata_borrow.rs:15:25
|
15 | _userdata_ref = userdata.borrow::<MyUserData>();
| ^^^^^^^^ borrowed value does not live long enough
16 | //~^ error: `userdata` does not live long enough
17 | }
| - `userdata` dropped here while still borrowed
18 | Ok(())
19 | }
| - borrow might be used here, when `_userdata_ref` is dropped and runs the destructor for type `std::result::Result<std::cell::Ref<'_, main::MyUserData>, mlua::error::Error>`
|
= note: values in a scope are dropped in the opposite order they are defined

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