Compare commits

...

81 Commits

Author SHA1 Message Date
Alex Orlenko 185fee956d v0.8.2 2022-07-25 14:23:25 +01:00
Alex Orlenko 4fc69be5f6 Update rustyline dev dependency 2022-07-25 14:14:01 +01:00
Alex Orlenko 3ec076693a Add FAQ 2022-07-24 11:59:04 +01:00
Alex Orlenko 95adf8df8e Add __iter to destructed metatable (luau) 2022-07-24 11:55:36 +01:00
Alex Orlenko 4a25eab257 Merge pull request #189 from hack3ric/master
`Function::bind`: simply clone the function if args are empty `MultiValue`
2022-07-22 00:28:18 +01:00
Alex Orlenko 40fe937878 Fix a bug in Function::bind when args and binds are empty
This leads to a Lua assertion due to using wrong stack index
2022-07-22 00:24:53 +01:00
Eric Long a6b178328d Merge branch 'master' of https://github.com/hack3ric/mlua 2022-07-21 20:55:28 +08:00
Eric Long f3f173fcb6 Function::bind: simply clone the function if args are empty MultiValue 2022-07-21 20:54:49 +08:00
Alex Orlenko d3b48cf2f3 Use Luau tags to mark userdata objects as destructed 2022-07-18 10:38:22 +01:00
Alex Orlenko f9ff6116db Use MaybeUninit instead of hack in protect_lua_closure 2022-07-17 11:33:51 +01:00
Alex Orlenko f75af6d75f Fix clippy warnings 2022-07-17 11:11:30 +01:00
Alex Orlenko 059e41bafb Optimize WrappedFailure userdata detection.
This is done by comparing a metatable pointer to previously saved one (it never changes).
2022-07-17 11:00:46 +01:00
Alex Orlenko f7ee6dc635 Add lua_xpush to 5.1-5.4 2022-07-17 11:00:46 +01:00
Alex Orlenko 0919ff21c9 Merge pull request #181 from hack3ric/master
Add `MultiValue::get` & add clear error in `Index`
2022-07-01 10:10:16 +01:00
Eric Long 553251761f Add MultiValue::get & add clear error in Index 2022-07-01 01:59:40 +08:00
Alex Orlenko b46b476f80 v0.8.1 2022-06-29 15:41:22 +01:00
Alex Orlenko 70e16b51ae Set source name to full file path in Luau require 2022-06-29 11:25:16 +01:00
Alex Orlenko 9596b97faa Update Lua::create_userdata doc about sharing metatable.
Closes #175
2022-06-29 00:25:26 +01:00
Alex Orlenko 8cbb3d8fae Merge pull request #180 from khvzak/userdata_proxy
Add Lua::create_proxy for easy access to UserData static fields and f…
2022-06-29 00:05:59 +01:00
Alex Orlenko e7f494530f Add Lua::create_proxy for easy access to UserData static fields and functions
Closes #178
2022-06-28 23:03:29 +01:00
Alex Orlenko 3746c3614f Merge pull request #179 from hack3ric/master
Implement utilities for MultiValue
2022-06-28 22:56:28 +01:00
Alex Orlenko 9af1aaf889 Make clippy happy 2022-06-28 21:28:48 +01:00
Eric Long c20eb20a59 Implement utilities for MultiValue 2022-06-29 01:21:32 +08:00
Alex Orlenko 04ba93137c Add Table::to_pointer() and String::to_pointer() functions 2022-06-27 14:58:48 +01:00
Alex Orlenko 113f91ace3 Derive Default for Lua::MemoryInfo 2022-06-27 13:57:15 +01:00
Alex Orlenko ff0d923aae Don't use custom allocator for non-vendored LuaJIT (fixes #176) 2022-06-27 13:04:32 +01:00
Alex Orlenko d9ac7abff2 v0.8.0 2022-06-08 23:57:35 +01:00
Alex Orlenko 48aa97351d Add Sandboxing section to README 2022-06-08 23:56:26 +01:00
Alex Orlenko 4516ca0bb5 Forgotten part of userdata performance optimization for Lua 5.1 2022-06-07 17:59:22 +01:00
Alex Orlenko 9005f32a98 Add ffi::luau::lua_tolightuserdata introduced in Luau 0.528 2022-06-06 22:40:29 +01:00
Alex Orlenko 93d36b9068 More performance optimization (userdata part) 2022-06-06 21:42:55 +01:00
Alex Orlenko f9f32bffce Update bundled luau/jit versions 2022-06-06 15:59:28 +01:00
Alex Orlenko da5010e6de Switch to 2021 edition and bump MSRV to 1.56+ 2022-06-06 15:40:29 +01:00
Alex Orlenko 4afca8f5bb Prepare for v0.8.0 release 2022-06-02 22:44:07 +01:00
Alex Orlenko b065db37c2 Always use Rust allocator for Lua 5.1/u 2022-05-31 02:09:58 +01:00
Alex Orlenko a100ebb3d7 Fix luaL_requiref glb handling for Lua<=5.1 2022-05-30 21:45:25 +01:00
Alex Orlenko a05a58b258 Update integer/number coercion logic 2022-05-30 19:26:12 +01:00
Alex Orlenko 3766cee4dd Experimental optimization to skip oom check 2022-05-30 18:42:34 +01:00
Alex Orlenko 99becdb590 More inline attributes 2022-05-29 20:17:09 +01:00
Alex Orlenko 799b4e9f0d Make Variadic::new and MultiValue::new const 2022-05-28 21:13:49 +01:00
Alex Orlenko 93f0b2a5de Update type of lua_Integer for lua51/52 2022-05-27 12:26:56 +01:00
Alex Orlenko 62db3adde1 Add "send" test 2022-05-26 22:22:23 +01:00
Alex Orlenko afa343ff08 Add [track_caller] attribute when working with app data container 2022-05-26 20:16:09 +01:00
Alex Orlenko 4f06f614a9 v0.8.0-beta.5 2022-05-25 01:47:38 +01:00
Alex Orlenko 30ba616a8a Correctly deserialize newtype struct (#168) 2022-05-24 23:26:17 +01:00
Alex Orlenko 0076aa735a Add Function::coverage for Luau to obtain coverage report 2022-05-24 02:01:46 +01:00
Alex Orlenko bcf2cbea37 Add Value::to_pointer() function.
Closes #165 and #166.
2022-05-18 13:15:08 +01:00
Alex Orlenko 8cd594c609 Fix typo 2022-05-18 12:15:39 +01:00
Alex Orlenko 8d71ea79aa Cache bytecode for internal Lua chunks 2022-05-16 20:29:02 +01:00
Alex Orlenko 2a8c5c7f82 Refactor Function::bind implementation.
Make it possible to bind async function arguments.
Fixes #161
2022-05-15 01:15:31 +01:00
Alex Orlenko 6b2ceb60c4 Fix Send impl 2022-05-12 19:24:12 +01:00
Alex Orlenko 3aac7131ee Add Iter metamethod for Luau (available from 0.526) 2022-05-09 14:34:10 +01:00
Alex Orlenko 1807fa789c Don't require Lua sources for modules in build system 2022-05-07 22:16:54 +01:00
Alex Orlenko 7db7526a08 Update to Luau 0.526 2022-05-07 02:08:50 +01:00
Alex Orlenko bea9b0f8b5 Assert stack for Luau in callback_error_ext when operating on a cached wrapped failure.
Should solve #153.
2022-04-27 00:20:25 +01:00
Alex Orlenko 32a3f478bd v0.8.0-beta.4 2022-04-25 23:18:27 +01:00
Alex Orlenko 294b3b7aae Bump mlua_derive to 0.8.0-beta.1 2022-04-19 23:55:16 +01:00
Alex Orlenko b0dabe9d3c Add parking_lot feature to README 2022-04-19 23:50:11 +01:00
Alex Orlenko 5133a9837a Add Function::info() to get information about functions.
Closes #149 and #7.
2022-04-18 18:28:11 +01:00
Alex Orlenko 790df77965 Fix "unused" warning in tests 2022-04-17 22:39:21 +01:00
Alex Orlenko a62636fde0 More parking_lot ci tests 2022-04-17 17:29:51 +01:00
Alex Orlenko d77c0e3b8d Merge pull request #147 from sondr3/parking_lot
Add parking_lot dependency and feature
2022-04-17 01:38:03 +01:00
Alex Orlenko 3904213ed0 Faster lua_rotate for Lua < 5.3 2022-04-17 01:02:40 +01:00
Sondre Aasemoen a5ce0c1409 Add parking_lot dependency and feature 2022-04-16 19:45:21 +02:00
Alex Orlenko dba76f3994 Merge pull request #145 from zh-jq/patch-v0.7.4
support luajit version 2.0.4
2022-04-15 20:25:32 +01:00
Alex Orlenko 86f506a170 Fix clippy warnings 2022-04-14 21:55:36 +01:00
Alex Orlenko d3975bdf30 Refactor AsChunk trait.
Remove blanket implementation for T: AsRef<[u8]>
Implement for `std::path::Path`
2022-04-14 20:48:00 +01:00
Alex Orlenko 21affdadfd Remove Luau compiler options from Chunk in favour of setting Compiler instance.
Add ability to set global Luau compiler used for load all chunks including via require function.
2022-04-14 00:54:29 +01:00
Alex Orlenko 17473269f8 Update collectgarbage for Luau: support more options 2022-04-13 20:50:50 +01:00
Alex Orlenko 88e3e92009 Define CallbackUpvalue/AsyncCallbackUpvalue as type alises to Upvalue 2022-04-13 18:29:51 +01:00
Alex Orlenko 1ba7a409d5 Update compile tests 2022-04-13 14:30:52 +01:00
Alex Orlenko ef81f40afa Add tests for static Lua instance 2022-04-13 14:30:45 +01:00
Alex Orlenko a018d3b6dc Revert "Remove Lua::into_static/from_static"
This reverts commit f75b7b7879.
2022-04-13 13:44:59 +01:00
Alex Orlenko 0215c31a3a Refactor Lua instance structure.
The idea is to keep same Lua instance across all calls and only change context inside callbacks.
This should solve #104.
2022-04-13 13:44:12 +01:00
Alex Orlenko 5cd82d0f6b Refactor main_state handling 2022-04-13 13:44:11 +01:00
zhangjingqiang 75697e3a3b support luajit version 2.0.4 2022-04-13 13:36:45 +08:00
Alex Orlenko d1c80be033 Don't cast *const to *mut (for pointers comparison) 2022-04-08 22:52:55 +01:00
Alex Orlenko a8fef51e86 (minor) Fix capturing variables in protect_lua closures 2022-04-08 21:15:57 +01:00
Alex Orlenko 28a063c1e5 Implement Hash for Lua String 2022-04-08 20:02:18 +01:00
Alex Orlenko 55fac90a74 Fix Luau documentation in docs.rs
Enable `Lua::gc_inc` for Luau
Mark `debug` module as safe for Luau
2022-04-08 20:02:12 +01:00
Alex Orlenko 70d287cf9f Don't pass Lua handler to interrupt callback (Luau) as it's not safe.
Optimize callback_error_ext to check stack only before allocating a new WrappedFailure.
2022-04-08 10:45:28 +01:00
70 changed files with 2039 additions and 892 deletions
+13 -13
View File
@@ -28,7 +28,7 @@ jobs:
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --features "${{ matrix.lua }},vendored"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-20.04' && matrix.lua != 'lua54' }}
@@ -39,7 +39,7 @@ jobs:
build_aarch64_cross_macos:
name: Cross-compile to aarch64-apple-darwin
runs-on: macos-11.0
runs-on: macos-latest
needs: build
strategy:
matrix:
@@ -52,7 +52,7 @@ jobs:
target: aarch64-apple-darwin
override: true
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
@@ -74,7 +74,7 @@ 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"
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
build_armv7_cross_ubuntu:
@@ -97,7 +97,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"
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
test:
@@ -127,13 +127,13 @@ 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,send,serialize,macros,parking_lot"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
run: |
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
shell: bash
test_with_sanitizer:
@@ -159,7 +159,7 @@ jobs:
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
RUSTFLAGS="-Z sanitizer=address" \
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
test_modules:
@@ -186,8 +186,8 @@ jobs:
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
shell: bash
test_modules_windows:
@@ -208,8 +208,8 @@ jobs:
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module && cargo test --release --features "${{ matrix.lua }}")
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
rustfmt:
name: Rustfmt
@@ -239,4 +239,4 @@ jobs:
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
+48
View File
@@ -1,3 +1,51 @@
## v0.8.2
- Performance optimizations in handling UserData
- Minimal Luau updated to 0.536
- Fixed bug in `Function::bind` when passing empty binds and no arguments (#189)
## v0.8.1
- Added `Lua::create_proxy` for accessing to UserData static fields and functions without instance
- Added `Table::to_pointer()` and `String::to_pointer()` functions
- Bugfixes and improvements (#176 #179)
## v0.8.0
Changes since 0.7.4
- Roblox Luau support
- Removed C glue
- Added async support to `__index` and `__newindex` metamethods
- Added `Function::info()` to get information about functions (#149).
- Added `parking_lot` dependency under feature flag (for `UserData`)
- `Hash` implementation for Lua String
- Added `Value::to_pointer()` function
- Performance improvements
Breaking changes:
- Refactored `AsChunk` trait (added implementation for `Path` and `PathBuf`).
## v0.8.0-beta.5
- Lua sources no longer needed to build modules
- Added `__iter` metamethod for Luau
- Added `Value::to_pointer()` function
- Added `Function::coverage` for Luau to obtain coverage report
- Bugfixes and improvements (#153 #161 #168)
## v0.8.0-beta.4
- Removed `&Lua` from `Lua::set_interrupt` as it's not safe (introduced in v0.8.0-beta.3)
- Enabled `Lua::gc_inc` for Luau
- Luau `debug` module marked as safe (enabled by default)
- Implemented `Hash` for Lua String
- Support mode options in `collectgarbage` for Luau
- Added ability to set global Luau compiler (used for loading all chunks).
- Refactored `AsChunk` trait (breaking changes).
`AsChunk` now implemented for `Path` and `PathBuf` to load lua files from fs.
- Added `parking_lot` dependency and feature flag (for `UserData`)
- Added `Function::info()` to get information about functions (#149).
- Bugfixes and improvements (#104 #142)
## v0.8.0-beta.3
- Luau vector constructor
+9 -10
View File
@@ -1,12 +1,12 @@
[package]
name = "mlua"
version = "0.8.0-beta.3" # remember to update html_root_url and mlua_derive
version = "0.8.2" # remember to update html_root_url and mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
edition = "2021"
repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua"
readme = "README.md"
keywords = ["lua", "luajit", "async", "futures", "scripting"]
keywords = ["lua", "luajit", "luau", "async", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
links = "lua"
@@ -17,14 +17,12 @@ with async/await features and support of writing native Lua modules in Rust.
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"examples/module",
"tests/module",
]
[features]
@@ -43,7 +41,7 @@ serialize = ["serde", "erased-serde"]
macros = ["mlua_derive/macros"]
[dependencies]
mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
@@ -53,16 +51,17 @@ futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true }
serde = { version = "1.0", optional = true }
erased-serde = { version = "0.3", optional = true }
parking_lot = { version = "0.12", optional = true }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
luau0-src = { version = "0.2.2", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.3.6", optional = true }
[dev-dependencies]
rustyline = "9.0"
rustyline = "10.0"
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
trybuild = "1.0"
futures = "0.3.5"
+21
View File
@@ -0,0 +1,21 @@
# mlua FAQ
This file is for general questions that don't fit into the README or crate docs.
## Loading a C module fails with error `undefined symbol: lua_xxx`. How to fix?
Add the following rustflags to your [.cargo/config](http://doc.crates.io/config.html) in order to properly export Lua symbols:
```toml
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
```
## I want to add support for a Lua VM fork to mlua. Do you accept pull requests?
Adding new feature flag to support a Lua VM fork is a major step that requires huge effort to maintain it.
Regular updates, testing, checking compatibility, etc.
That's why I don't plan to support new Lua VM forks or other languages in mlua.
+18 -4
View File
@@ -9,9 +9,13 @@
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/khvzak/mlua
[MSRV]: https://img.shields.io/badge/rust-1.53+-brightgreen.svg?&logo=rust
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
[Guided Tour](examples/guided_tour.rs)
[Guided Tour] | [Benchmarks] | [FAQ]
[Guided Tour]: examples/guided_tour.rs
[Benchmarks]: https://github.com/khvzak/script-bench-rs
[FAQ]: FAQ.md
`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.
@@ -43,6 +47,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
* `macros`: enable procedural macros (such as `chunk!`)
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
[5.4]: https://www.lua.org/manual/5.4/manual.html
[5.3]: https://www.lua.org/manual/5.3/manual.html
@@ -56,6 +61,7 @@ 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
@@ -104,7 +110,7 @@ Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = { version = "0.8.0-beta.3", features = ["lua54", "vendored"] }
mlua = { version = "0.8", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -139,7 +145,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.8.0-beta.3", features = ["lua54", "vendored", "module"] }
mlua = { version = "0.8", features = ["lua54", "vendored", "module"] }
```
`lib.rs` :
@@ -254,6 +260,14 @@ If you encounter them, a bug report would be very welcome:
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
## Sandboxing
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in controlled environment.
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
[Luau Sandboxing]: https://luau-lang.org/sandbox
## License
This project is licensed under the [MIT license](LICENSE)
+1 -1
View File
@@ -1,5 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
unreachable!()
None
}
+3 -1
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
use std::path::PathBuf;
@@ -79,7 +81,7 @@ pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.0.5"), Bound::Unbounded))
.range_version((Bound::Included("2.0.4"), Bound::Unbounded))
.cargo_metadata(need_lua_lib)
.probe("luajit");
+2 -1
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
@@ -20,7 +22,6 @@ pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new().build();
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata();
Some(artifacts.include_dir().to_owned())
+4
View File
@@ -105,6 +105,10 @@ fn main() {
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
#[cfg(all(feature = "luau", feature = "module"))]
compile_error!("Luau does not support module mode");
#[cfg(any(not(feature = "module"), target_os = "windows"))]
find::probe_lua();
println!("cargo:rerun-if-changed=build");
+2 -1
View File
@@ -7,13 +7,14 @@ edition = "2018"
[lib]
crate-type = ["cdylib"]
[workspace]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../..", features = ["module"] }
-22
View File
@@ -8,32 +8,10 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
Ok(ud.0)
}
#[mlua::lua_module]
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
Ok(exports)
}
#[derive(Clone, Copy)]
struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module]
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
Ok(exports)
}
#[mlua::lua_module]
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".to_lua_err())
}
+1 -1
View File
@@ -5,7 +5,7 @@ use rustyline::Editor;
fn main() {
let lua = Lua::new();
let mut editor = Editor::<()>::new();
let mut editor = Editor::<()>::new().expect("Failed to make rustyline editor");
loop {
let mut prompt = "> ";
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.6.0"
version = "0.8.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
description = "Procedural macros for the mlua crate."
@@ -21,4 +21,4 @@ proc-macro-error = { version = "1.0", optional = true }
syn = { version = "1.0", features = ["full"] }
itertools = { version = "0.10", optional = true }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.5", optional = true }
once_cell = { version = "1.0", optional = true }
+4 -2
View File
@@ -62,6 +62,8 @@ pub fn chunk(input: TokenStream) -> TokenStream {
let wrapped_code = quote! {{
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
use ::std::borrow::Cow;
use ::std::io::Result as IoResult;
use ::std::marker::PhantomData;
use ::std::sync::Mutex;
@@ -73,8 +75,8 @@ pub fn chunk(input: TokenStream) -> TokenStream {
where
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
{
fn source(&self) -> &[u8] {
(#source).as_bytes()
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
+160 -135
View File
@@ -1,5 +1,9 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::CString;
use std::io::Result as IoResult;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::ffi;
@@ -16,10 +20,10 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
/// [`Chunk`]: crate::Chunk
pub trait AsChunk<'lua> {
/// Returns chunk data (can be text or binary)
fn source(&self) -> &[u8];
fn source(&self) -> IoResult<Cow<[u8]>>;
/// Returns optional chunk name
fn name(&self) -> Option<CString> {
fn name(&self) -> Option<StdString> {
None
}
@@ -36,9 +40,47 @@ pub trait AsChunk<'lua> {
}
}
impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
fn source(&self) -> &[u8] {
self.as_ref()
impl<'lua> AsChunk<'lua> for str {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl<'lua> AsChunk<'lua> for StdString {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl<'lua> AsChunk<'lua> for [u8] {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl<'lua> AsChunk<'lua> for Vec<u8> {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl<'lua> AsChunk<'lua> for Path {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
}
impl<'lua> AsChunk<'lua> for PathBuf {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
}
@@ -48,8 +90,8 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
pub(crate) lua: &'lua Lua,
pub(crate) source: Cow<'a, [u8]>,
pub(crate) name: Option<CString>,
pub(crate) source: IoResult<Cow<'a, [u8]>>,
pub(crate) name: Option<StdString>,
pub(crate) env: Result<Option<Value<'lua>>>,
pub(crate) mode: Option<ChunkMode>,
#[cfg(feature = "luau")]
@@ -104,7 +146,7 @@ impl Compiler {
/// * 0 - no optimization
/// * 1 - baseline optimization level that doesn't prevent debuggability (default)
/// * 2 - includes optimizations that harm debuggability such as inlining
pub fn set_optimization_level(&mut self, level: u8) -> &mut Self {
pub fn set_optimization_level(mut self, level: u8) -> Self {
self.optimization_level = level;
self
}
@@ -115,7 +157,7 @@ impl Compiler {
/// * 0 - no debugging support
/// * 1 - line info & function names only; sufficient for backtraces (default)
/// * 2 - full debug info with local & upvalue names; necessary for debugger
pub fn set_debug_level(&mut self, level: u8) -> &mut Self {
pub fn set_debug_level(mut self, level: u8) -> Self {
self.debug_level = level;
self
}
@@ -126,19 +168,19 @@ impl Compiler {
/// * 0 - no code coverage support (default)
/// * 1 - statement coverage
/// * 2 - statement and expression coverage (verbose)
pub fn set_coverage_level(&mut self, level: u8) -> &mut Self {
pub fn set_coverage_level(mut self, level: u8) -> Self {
self.coverage_level = level;
self
}
#[doc(hidden)]
pub fn set_vector_lib(&mut self, lib: Option<String>) -> &mut Self {
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
self.vector_lib = lib;
self
}
#[doc(hidden)]
pub fn set_vector_ctor(&mut self, ctor: Option<String>) -> &mut Self {
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
self.vector_ctor = ctor;
self
}
@@ -146,7 +188,7 @@ impl Compiler {
/// Sets a list of globals that are mutable.
///
/// It disables the import optimization for fields accessed through these.
pub fn set_mutable_globals(&mut self, globals: Vec<String>) -> &mut Self {
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
self.mutable_globals = globals;
self
}
@@ -174,7 +216,7 @@ impl Compiler {
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let mut mutable_globals_ptr = ptr::null_mut();
if mutable_globals.len() > 0 {
if !mutable_globals.is_empty() {
mutable_globals.push(ptr::null());
mutable_globals_ptr = mutable_globals.as_mut_ptr();
}
@@ -195,14 +237,10 @@ impl Compiler {
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name<S: AsRef<[u8]> + ?Sized>(mut self, name: &S) -> Result<Self> {
let name =
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
from: "&str",
to: "string",
message: Some(e.to_string()),
})?;
self.name = Some(name);
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
self.name = Some(name.as_ref().to_string());
// Do extra validation
let _ = self.convert_name()?;
Ok(self)
}
@@ -232,96 +270,15 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self
}
/// Sets Luau compiler optimization level.
/// Sets or overwrites a Luau compiler used for this chunk.
///
/// See [`Compiler::set_optimization_level`] for details.
/// See [`Compiler`] for details and possible options.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_optimization_level(mut self, level: u8) -> Self {
self.compiler
.get_or_insert_with(Default::default)
.set_optimization_level(level);
self
}
/// Sets Luau compiler debug level.
///
/// See [`Compiler::set_debug_level`] for details.
///
/// Requires `feature = "luau`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_debug_level(mut self, level: u8) -> Self {
self.compiler
.get_or_insert_with(Default::default)
.set_debug_level(level);
self
}
/// Sets Luau compiler code coverage level.
///
/// See [`Compiler::set_coverage_level`] for details.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_coverage_level(mut self, level: u8) -> Self {
self.compiler
.get_or_insert_with(Default::default)
.set_coverage_level(level);
self
}
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
self.compiler
.get_or_insert_with(Default::default)
.set_vector_lib(lib);
self
}
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
self.compiler
.get_or_insert_with(Default::default)
.set_vector_ctor(ctor);
self
}
/// Sets a list of globals that are mutable for Luau compiler.
///
/// See [`Compiler::set_mutable_globals`] for details.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
self.compiler
.get_or_insert_with(Default::default)
.set_mutable_globals(globals);
self
}
/// Compiles the chunk and changes mode to binary.
///
/// It does nothing if the chunk is already binary.
#[cfg(feature = "luau")]
#[doc(hidden)]
pub fn compile(mut self) -> Self {
if self.detect_mode() == ChunkMode::Text {
let data = self
.compiler
.get_or_insert_with(Default::default)
.compile(self.source.as_ref());
self.mode = Some(ChunkMode::Binary);
self.source = Cow::Owned(data);
}
pub fn set_compiler(mut self, compiler: Compiler) -> Self {
self.compiler = Some(compiler);
self
}
@@ -422,38 +379,89 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Load this chunk into a regular `Function`.
///
/// This simply compiles the chunk without actually executing it.
pub fn into_function(self) -> Result<Function<'lua>> {
#[cfg(not(feature = "luau"))]
let self_ = self;
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
pub fn into_function(mut self) -> Result<Function<'lua>> {
#[cfg(feature = "luau")]
let self_ = match self.compiler {
// We don't need to compile source if no compiler options set
Some(_) => self.compile(),
_ => self,
};
if self.compiler.is_some() {
// We don't need to compile source if no compiler set
self.compile();
}
self_.lua.load_chunk(
self_.source.as_ref(),
self_.name.as_ref(),
self_.env()?,
self_.mode,
)
let name = self.convert_name()?;
self.lua
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
}
fn env(&self) -> Result<Option<Value<'lua>>> {
self.env.clone()
/// Compiles the chunk and changes mode to binary.
///
/// It does nothing if the chunk is already binary.
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);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
#[cfg(not(feature = "luau"))]
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
let data = func.dump(false);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
}
}
}
fn expression_source(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(b"return ".len() + self.source.len());
buf.extend(b"return ");
buf.extend(self.source.as_ref());
buf
/// Fetches compiled bytecode of this chunk from the cache.
///
/// If not found, compiles the source code and stores it on the cache.
pub(crate) fn try_cache(mut self) -> Self {
struct ChunksCache(HashMap<Vec<u8>, Vec<u8>>);
// Try to fetch compiled chunk from cache
let mut text_source = None;
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
if let Some(cache) = self.lua.app_data_ref::<ChunksCache>() {
if let Some(data) = cache.0.get(source.as_ref()) {
self.source = Ok(Cow::Owned(data.clone()));
self.mode = Some(ChunkMode::Binary);
return self;
}
}
text_source = Some(source.as_ref().to_vec());
}
}
// Compile and cache the chunk
if let Some(text_source) = text_source {
self.compile();
if let Ok(ref binary_source) = self.source {
if self.detect_mode() == ChunkMode::Binary {
if let Some(mut cache) = self.lua.app_data_mut::<ChunksCache>() {
cache.0.insert(text_source, binary_source.as_ref().to_vec());
} else {
let mut cache = ChunksCache(HashMap::new());
cache.0.insert(text_source, binary_source.as_ref().to_vec());
self.lua.set_app_data(cache);
}
}
}
}
self
}
fn to_expression(&self) -> Result<Function<'lua>> {
// We assume that mode is Text
let source = self.expression_source();
let source = self.source.as_ref();
let source = source.map_err(|err| Error::RuntimeError(err.to_string()))?;
let source = Self::expression_source(source);
// We don't need to compile source if no compiler options set
#[cfg(feature = "luau")]
let source = self
@@ -462,24 +470,41 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
.map(|c| c.compile(&source))
.unwrap_or(source);
let name = self.convert_name()?;
self.lua
.load_chunk(&source, self.name.as_ref(), self.env()?, None)
.load_chunk(&source, name.as_deref(), self.env.clone()?, None)
}
fn detect_mode(&self) -> ChunkMode {
match self.mode {
Some(mode) => mode,
None => {
match (self.mode, &self.source) {
(Some(mode), _) => mode,
(None, Ok(source)) => {
#[cfg(not(feature = "luau"))]
if self.source.starts_with(ffi::LUA_SIGNATURE) {
if source.starts_with(ffi::LUA_SIGNATURE) {
return ChunkMode::Binary;
}
#[cfg(feature = "luau")]
if self.source[0] < b'\n' {
if *source.first().unwrap_or(&u8::MAX) < b'\n' {
return ChunkMode::Binary;
}
ChunkMode::Text
}
(None, Err(_)) => ChunkMode::Text, // any value is fine
}
}
fn convert_name(&self) -> Result<Option<CString>> {
self.name
.clone()
.map(CString::new)
.transpose()
.map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
}
fn expression_source(source: &[u8]) -> Vec<u8> {
let mut buf = Vec::with_capacity(b"return ".len() + source.len());
buf.extend(b"return ");
buf.extend(source);
buf
}
}
+47 -14
View File
@@ -250,18 +250,21 @@ impl<'lua> ToLua<'lua> for &str {
}
impl<'lua> ToLua<'lua> for Cow<'_, str> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> ToLua<'lua> for Box<str> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&*self)?))
}
}
impl<'lua> FromLua<'lua> for Box<str> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(lua
@@ -278,12 +281,14 @@ impl<'lua> FromLua<'lua> for Box<str> {
}
impl<'lua> ToLua<'lua> for CString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> FromLua<'lua> for CString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
let string = lua
@@ -306,24 +311,28 @@ impl<'lua> FromLua<'lua> for CString {
}
impl<'lua> ToLua<'lua> for &CStr {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> ToLua<'lua> for BString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
impl<'lua> FromLua<'lua> for BString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(BString::from(
@@ -340,14 +349,16 @@ impl<'lua> FromLua<'lua> for BString {
}
impl<'lua> ToLua<'lua> for &BStr {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
Ok(Value::String(lua.create_string(self)?))
}
}
macro_rules! lua_convert_int {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.map(Value::Integer)
@@ -362,22 +373,27 @@ macro_rules! lua_convert_int {
}
impl<'lua> FromLua<'lua> for $x {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
(if let Value::Integer(i) = value {
cast(i)
} else if let Some(i) = lua.coerce_integer(value.clone())? {
cast(i)
} else {
cast(lua.coerce_number(value)?.ok_or_else(|| {
Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
message: Some(
"expected number or string coercible to number".to_string(),
),
(match value {
Value::Integer(i) => cast(i),
Value::Number(n) => cast(n),
_ => {
if let Some(i) = lua.coerce_integer(value.clone())? {
cast(i)
} else {
cast(lua.coerce_number(value)?.ok_or_else(|| {
Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
message: Some(
"expected number or string coercible to number".to_string(),
),
}
})?)
}
})?)
}
})
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
@@ -405,6 +421,7 @@ lua_convert_int!(usize);
macro_rules! lua_convert_float {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
@@ -417,6 +434,7 @@ macro_rules! lua_convert_float {
}
impl<'lua> FromLua<'lua> for $x {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
lua.coerce_number(value)?
@@ -444,6 +462,7 @@ impl<'lua, T> ToLua<'lua> for &[T]
where
T: Clone + ToLua<'lua>,
{
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
@@ -455,6 +474,7 @@ impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
where
T: ToLua<'lua>,
{
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
@@ -464,6 +484,7 @@ impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
where
T: FromLua<'lua>,
{
#[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
@@ -496,24 +517,28 @@ where
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
}
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
}
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
#[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
@@ -535,6 +560,7 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
for HashMap<K, V, S>
{
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
@@ -543,6 +569,7 @@ impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lu
impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
for HashMap<K, V, S>
{
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
if let Value::Table(table) = value {
table.pairs().collect()
@@ -557,12 +584,14 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Defa
}
impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
}
impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<K, V> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
if let Value::Table(table) = value {
table.pairs().collect()
@@ -577,6 +606,7 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
}
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
@@ -585,6 +615,7 @@ impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T
}
impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua> for HashSet<T, S> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
@@ -602,6 +633,7 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
}
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
@@ -610,6 +642,7 @@ impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
}
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
+9 -3
View File
@@ -155,6 +155,13 @@ pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
@@ -561,11 +568,10 @@ pub unsafe fn luaL_requiref(
lua_getfield(L, -1, modname);
}
}
if cfg!(feature = "lua51") && glb != 0 {
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
}
if cfg!(feature = "luajit") && glb == 0 {
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
+10 -1
View File
@@ -67,7 +67,10 @@ pub const LUA_MINSTACK: c_int = 20;
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = isize;
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
@@ -321,6 +324,12 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
+7
View File
@@ -27,6 +27,13 @@ unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
+10 -1
View File
@@ -69,7 +69,10 @@ pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = isize;
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// A Lua unsigned integer, equivalent to `u32` in Lua 5.2
pub type lua_Unsigned = c_uint;
@@ -407,6 +410,12 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
+6
View File
@@ -434,6 +434,12 @@ pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_pop(L, 1)
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
+6
View File
@@ -455,6 +455,12 @@ pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_pop(L, 1)
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
#[inline(always)]
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatauv(L, sz, 1)
+10
View File
@@ -90,6 +90,13 @@ unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_De
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
@@ -506,6 +513,9 @@ pub unsafe fn luaL_requiref(
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
+13 -2
View File
@@ -133,6 +133,7 @@ extern "C" {
pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char;
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
@@ -259,7 +260,12 @@ extern "C" {
pub fn lua_concat(L: *mut lua_State, n: c_int);
// TODO: lua_encodepointer
pub fn lua_clock() -> c_double;
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Udestructor>);
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
pub fn lua_setuserdatadtor(
L: *mut lua_State,
tag: c_int,
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
);
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
}
@@ -432,7 +438,12 @@ extern "C" {
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int);
pub fn lua_breakpoint(
L: *mut lua_State,
funcindex: c_int,
line: c_int,
enabled: c_int,
) -> c_int;
pub fn lua_getcoverage(
L: *mut lua_State,
+150 -25
View File
@@ -1,10 +1,14 @@
use std::os::raw::c_int;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
use crate::util::{
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(feature = "async")]
@@ -14,6 +18,29 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
#[derive(Clone, Debug)]
pub struct FunctionInfo {
pub name: Option<Vec<u8>>,
pub name_what: Option<Vec<u8>>,
pub what: Option<Vec<u8>>,
pub source: Option<Vec<u8>>,
pub short_src: Option<Vec<u8>>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
}
/// Luau function coverage snapshot.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoverageInfo {
pub function: Option<std::string::String>,
pub line_defined: i32,
pub depth: i32,
pub hits: Vec<i32>,
}
impl<'lua> Function<'lua> {
/// Calls the function, passing `args` as function arguments.
///
@@ -163,24 +190,19 @@ impl<'lua> Function<'lua> {
/// # }
/// ```
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
ffi::luaL_checkstack(state, nbinds + 2, ptr::null());
ffi::lua_settop(state, nargs + nbinds + 1);
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
ffi::lua_replace(state, 1);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
ffi::luaL_checkstack(state, nbinds, ptr::null());
for i in 0..nbinds {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
ffi::lua_replace(state, i + 2);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
}
if nargs > 0 {
ffi::lua_rotate(state, 1, nbinds);
}
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
ffi::lua_gettop(state)
nargs + nbinds
}
let lua = self.0.lua;
@@ -188,24 +210,74 @@ impl<'lua> Function<'lua> {
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
if nargs + 2 > ffi::LUA_MAX_UPVALUES {
if nargs == 0 {
return Ok(self.clone());
}
if nargs + 1 > ffi::LUA_MAX_UPVALUES {
return Err(Error::BindError);
}
unsafe {
let args_wrapper = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 5)?;
check_stack(lua.state, nargs + 3)?;
lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(arg)?;
}
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
})?;
Ok(Function(lua.pop_ref()))
Function(lua.pop_ref())
};
lua.load(
r#"
local func, args_wrapper = ...
return function(...)
return func(args_wrapper(...))
end
"#,
)
.try_cache()
.set_name("_mlua_bind")?
.call((self.clone(), args_wrapper))
}
/// Returns information about the function.
///
/// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function.
///
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
pub fn info(&self) -> FunctionInfo {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
let mut ar: ffi::lua_Debug = mem::zeroed();
lua.push_ref(&self.0);
#[cfg(not(feature = "luau"))]
let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar);
#[cfg(feature = "luau")]
let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar);
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()),
#[cfg(feature = "luau")]
name_what: None,
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
short_src: ptr_to_cstr_bytes(&ar.short_src as *const _).map(|s| s.to_vec()),
line_defined: ar.linedefined as i32,
#[cfg(not(feature = "luau"))]
last_line_defined: ar.lastlinedefined as i32,
}
}
}
@@ -213,12 +285,13 @@ impl<'lua> Function<'lua> {
///
/// If `strip` is true, the binary representation may not include all debug information
/// about the function, to save space.
///
/// For Luau a [Compiler] can be used to compile Lua chunks to bytecode.
///
/// [Compiler]: crate::chunk::Compiler
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn dump(&self, strip: bool) -> Vec<u8> {
use std::os::raw::c_void;
use std::slice;
unsafe extern "C" fn writer(
_state: *mut ffi::lua_State,
buf: *const c_void,
@@ -246,6 +319,58 @@ impl<'lua> Function<'lua> {
data
}
/// Retrieves recorded coverage information about this Lua function including inner calls.
///
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
/// per each executed inner function.
///
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
///
/// Requires `feature = "luau"`
///
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
#[cfg(any(feature = "luau", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn coverage<F>(&self, mut func: F)
where
F: FnMut(CoverageInfo),
{
use std::ffi::CStr;
use std::os::raw::c_char;
unsafe extern "C" fn callback<F: FnMut(CoverageInfo)>(
data: *mut c_void,
function: *const c_char,
line_defined: c_int,
depth: c_int,
hits: *const c_int,
size: usize,
) {
let function = if !function.is_null() {
Some(CStr::from_ptr(function).to_string_lossy().to_string())
} else {
None
};
let rust_callback = &mut *(data as *mut F);
rust_callback(CoverageInfo {
function,
line_defined,
depth,
hits: slice::from_raw_parts(hits, size).to_vec(),
});
}
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let func_ptr = &mut func as *mut F as *mut c_void;
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
}
}
}
impl<'lua> PartialEq for Function<'lua> {
+7 -15
View File
@@ -1,11 +1,11 @@
use std::cell::UnsafeCell;
use std::ffi::CStr;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
use std::os::raw::{c_char, c_int};
use std::os::raw::c_int;
use crate::ffi::{self, lua_Debug};
use crate::lua::Lua;
use crate::util::ptr_to_cstr_bytes;
/// Contains information about currently executing Lua code.
///
@@ -77,9 +77,9 @@ impl<'lua> Debug<'lua> {
);
DebugNames {
name: ptr_to_str((*self.ar.get()).name),
name: ptr_to_cstr_bytes((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_str((*self.ar.get()).namewhat),
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
#[cfg(feature = "luau")]
name_what: None,
}
@@ -101,12 +101,12 @@ impl<'lua> Debug<'lua> {
);
DebugSource {
source: ptr_to_str((*self.ar.get()).source),
short_src: ptr_to_str((*self.ar.get()).short_src.as_ptr()),
source: ptr_to_cstr_bytes((*self.ar.get()).source),
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
line_defined: (*self.ar.get()).linedefined as i32,
#[cfg(not(feature = "luau"))]
last_line_defined: (*self.ar.get()).lastlinedefined as i32,
what: ptr_to_str((*self.ar.get()).what),
what: ptr_to_cstr_bytes((*self.ar.get()).what),
}
}
}
@@ -349,11 +349,3 @@ impl BitOrAssign for HookTriggers {
*self = *self | rhs;
}
}
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
if input.is_null() {
None
} else {
Some(CStr::from_ptr(input).to_bytes())
}
}
+3 -3
View File
@@ -72,7 +72,7 @@
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.8.0-beta.3")]
#![doc(html_root_url = "https://docs.rs/mlua/0.8.2")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
@@ -108,7 +108,7 @@ pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::Function;
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::lua::{GCMode, Lua, LuaOptions};
pub use crate::multi::Variadic;
@@ -128,7 +128,7 @@ pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{chunk::Compiler, types::VmState};
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
+484 -324
View File
File diff suppressed because it is too large Load Diff
+29 -11
View File
@@ -29,26 +29,42 @@ impl Lua {
unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
let option = CStr::from_ptr(option);
let arg = ffi::luaL_optinteger(state, 2, 0);
match option.to_str() {
Ok("collect") => {
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
0
}
Ok("stop") => {
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
0
}
Ok("restart") => {
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
0
}
Ok("count") => {
let n = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0);
ffi::lua_pushnumber(state, n as ffi::lua_Number);
let kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0) as ffi::lua_Number;
let kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0) as ffi::lua_Number;
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
1
}
// TODO: More variants
_ => ffi::luaL_error(
state,
cstr!("collectgarbage must be called with 'count' or 'collect'"),
),
Ok("step") => {
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg);
ffi::lua_pushboolean(state, res);
1
}
Ok("isrunning") => {
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
ffi::lua_pushboolean(state, res);
1
}
_ => ffi::luaL_error(state, cstr!("collectgarbage called with invalid option")),
}
}
fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
let name = name.ok_or_else(|| Error::RuntimeError("name is nil".into()))?;
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
// Find module in the cache
let loaded = unsafe {
@@ -69,10 +85,12 @@ fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
search_path = "?.luau;?.lua".into();
}
let mut source = None;
let (mut source, mut source_name) = (None, String::new());
for path in search_path.split(';') {
if let Ok(buf) = std::fs::read(path.replacen('?', &name, 1)) {
let file_path = path.replacen('?', &name, 1);
if let Ok(buf) = std::fs::read(&file_path) {
source = Some(buf);
source_name = file_path;
break;
}
}
@@ -80,7 +98,7 @@ fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
let value = lua
.load(&source)
.set_name(&format!("={}", name))?
.set_name(&format!("={}", source_name))?
.set_mode(ChunkMode::Text)
.call::<_, Value>(())?;
+12 -1
View File
@@ -11,6 +11,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::new_or_cached(lua);
match self {
@@ -25,6 +26,7 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new_or_cached(lua);
v.push_front(self.to_lua(lua)?);
@@ -33,6 +35,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
lua.cache_multivalue(values);
@@ -41,12 +44,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
}
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
#[inline]
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(self)
}
}
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
#[inline]
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(values)
}
@@ -83,7 +88,7 @@ pub struct Variadic<T>(Vec<T>);
impl<T> Variadic<T> {
/// Creates an empty `Variadic` wrapper containing no values.
pub fn new() -> Variadic<T> {
pub const fn new() -> Variadic<T> {
Variadic(Vec::new())
}
}
@@ -124,6 +129,7 @@ impl<T> DerefMut for Variadic<T> {
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut values = MultiValue::new_or_cached(lua);
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
@@ -132,6 +138,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let res = values
.drain_all()
@@ -146,12 +153,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
macro_rules! impl_tuple {
() => (
impl<'lua> ToLuaMulti<'lua> for () {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_cached(lua))
}
}
impl<'lua> FromLuaMulti<'lua> for () {
#[inline]
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
lua.cache_multivalue(values);
Ok(())
@@ -166,6 +175,7 @@ macro_rules! impl_tuple {
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let ($($name,)* $last,) = self;
@@ -181,6 +191,7 @@ macro_rules! impl_tuple {
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
$(let $name = values.pop_front().unwrap_or(Nil);)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
+8 -8
View File
@@ -4,13 +4,13 @@
pub use crate::{
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
UserData as LuaUserData, UserDataFields as LuaUserDataFields,
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, LuaOptions,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs,
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua,
ToLuaMulti, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
Value as LuaValue,
};
@@ -21,7 +21,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::VmState as LuaVmState;
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
#[cfg(feature = "async")]
#[doc(no_inline)]
+6 -4
View File
@@ -355,13 +355,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
UserDataCell::new(data.clone()),
true,
)?;
ffi::lua_touserdata(lua.state, -1)
};
// Prepare metatable, add meta methods first and then meta fields
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
push_table(lua.state, 0, meta_methods_nrec as c_int)?;
push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
for (k, m) in ud_methods.meta_methods {
let data = data.clone();
@@ -377,7 +378,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_getters_index = None;
let field_getters_nrec = ud_fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(lua.state, 0, field_getters_nrec as c_int)?;
push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_getters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@@ -389,7 +390,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_setters_index = None;
let field_setters_nrec = ud_fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(lua.state, 0, field_setters_nrec as c_int)?;
push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_setters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@@ -402,7 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let methods_nrec = ud_methods.methods.len();
if methods_nrec > 0 {
// Create table used for methods lookup
push_table(lua.state, 0, methods_nrec as c_int)?;
push_table(lua.state, 0, methods_nrec as c_int, true)?;
for (k, m) in ud_methods.methods {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@@ -596,6 +597,7 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
}
}
#[allow(clippy::type_complexity)]
enum NonStaticMethod<'lua, T> {
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
+11 -9
View File
@@ -8,7 +8,6 @@ use rustc_hash::FxHashSet;
use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result};
use crate::ffi;
use crate::table::{Table, TablePairs, TableSequence};
use crate::value::Value;
@@ -320,10 +319,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
self.deserialize_map(visitor)
}
#[inline]
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf unit unit_struct newtype_struct
identifier ignored_any
byte_buf unit unit_struct identifier ignored_any
}
}
@@ -556,9 +562,7 @@ impl RecursionGuard {
#[inline]
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
let visited = Rc::clone(visited);
let lua = table.0.lua;
let ptr =
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
let ptr = table.to_pointer();
visited.borrow_mut().insert(ptr);
RecursionGuard { ptr, visited }
}
@@ -578,9 +582,7 @@ fn check_value_if_skip(
) -> Result<bool> {
match value {
Value::Table(table) => {
let lua = table.0.lua;
let ptr =
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
let ptr = table.to_pointer();
if visited.borrow().contains(&ptr) {
if options.deny_recursive_tables {
return Err(de::Error::custom("recursive table detected"));
+3 -1
View File
@@ -158,6 +158,7 @@ pub trait LuaSerdeExt<'lua> {
/// Ok(())
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
/// Deserializes a [`Value`] into any serde deserializable object with options.
@@ -189,6 +190,7 @@ pub trait LuaSerdeExt<'lua> {
/// Ok(())
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value_with<T: Deserialize<'lua>>(
&'lua self,
value: Value<'lua>,
@@ -257,7 +259,7 @@ pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
}
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
}
+11 -4
View File
@@ -327,10 +327,17 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
lua.push_ref(&self.table.0);
lua.push_value(value)?;
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
ffi::lua_rawseti(lua.state, -2, len + 1);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
}
+5
View File
@@ -46,12 +46,14 @@ impl StdLib {
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const JIT: StdLib = StdLib(1 << 9);
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const FFI: StdLib = StdLib(1 << 30);
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
pub const DEBUG: StdLib = StdLib(1 << 31);
@@ -61,7 +63,10 @@ impl StdLib {
/// (**unsafe**) All standard libraries
pub const ALL: StdLib = StdLib(u32::MAX);
/// The safe subset of the standard libraries
#[cfg(not(feature = "luau"))]
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);
#[cfg(feature = "luau")]
pub const ALL_SAFE: StdLib = StdLib(u32::MAX);
pub fn contains(self, lib: Self) -> bool {
(self & lib).0 != 0
+28 -1
View File
@@ -1,4 +1,6 @@
use std::borrow::Cow;
use std::borrow::{Borrow, Cow};
use std::hash::{Hash, Hasher};
use std::os::raw::c_void;
use std::string::String as StdString;
use std::{slice, str};
@@ -111,6 +113,17 @@ impl<'lua> String<'lua> {
slice::from_raw_parts(data as *const u8, size + 1)
}
}
/// Converts the string to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
let lua = self.0.lua;
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) }
}
}
impl<'lua> AsRef<[u8]> for String<'lua> {
@@ -119,6 +132,12 @@ impl<'lua> AsRef<[u8]> for String<'lua> {
}
}
impl<'lua> Borrow<[u8]> for String<'lua> {
fn borrow(&self) -> &[u8] {
self.as_bytes()
}
}
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
//
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str`, `String` and `mlua::String`
@@ -136,6 +155,14 @@ where
}
}
impl<'lua> Eq for String<'lua> {}
impl<'lua> Hash for String<'lua> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for String<'lua> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
+24 -6
View File
@@ -1,10 +1,11 @@
use std::marker::PhantomData;
use std::os::raw::c_void;
#[cfg(feature = "serialize")]
use {
rustc_hash::FxHashSet,
serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
std::{cell::RefCell, os::raw::c_void, result::Result as StdResult},
std::{cell::RefCell, result::Result as StdResult},
};
use crate::error::{Error, Result};
@@ -198,7 +199,13 @@ impl<'lua> Table<'lua> {
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
if lua.unlikely_memory_error() {
ffi::lua_rawset(lua.state, -3);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
}
}
}
@@ -351,7 +358,7 @@ impl<'lua> Table<'lua> {
/// Sets `readonly` attribute on the table.
///
/// Requires `feature = "luau"`
#[cfg(feature = "luau")]
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_readonly(&self, enabled: bool) {
let lua = self.0.lua;
@@ -369,13 +376,25 @@ impl<'lua> Table<'lua> {
/// Returns `readonly` attribute of the table.
///
/// Requires `feature = "luau"`
#[cfg(feature = "luau")]
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn is_readonly(&self) -> bool {
let lua = self.0.lua;
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_getreadonly(refthr, self.0.index) != 0) }
}
/// Converts the table to a generic C pointer.
///
/// Different tables will give different pointers.
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
let lua = self.0.lua;
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) }
}
/// Consume this table and return an iterator over the pairs of the table.
///
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
@@ -693,8 +712,7 @@ impl<'lua> Serialize for Table<'lua> {
static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
}
let lua = self.0.lua;
let ptr = unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) };
let ptr = self.to_pointer();
let res = VISITED.with(|visited| {
{
let mut visited = visited.borrow_mut();
+4 -9
View File
@@ -24,7 +24,6 @@ use {
std::{
cell::RefCell,
marker::PhantomData,
os::raw::c_void,
pin::Pin,
task::{Context, Poll, Waker},
},
@@ -299,9 +298,8 @@ impl<'lua> Thread<'lua> {
/// # Examples
///
/// ```
/// # #[cfg(feature = "luau")]
/// # fn main() -> mlua::Result<()> {
/// use mlua::Lua;
/// # use mlua::{Lua, Result};
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
/// lua2.load("var = 123").exec()?;
@@ -315,13 +313,10 @@ impl<'lua> Thread<'lua> {
/// assert_eq!(lua.globals().get::<_, Option<u32>>("var")?, None);
/// # Ok(())
/// # }
///
/// # #[cfg(not(feature = "luau"))]
/// fn main() {}
/// ```
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg(any(feature = "luau", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
@@ -440,7 +435,7 @@ where
fn is_poll_pending(val: &MultiValue) -> bool {
match val.iter().enumerate().last() {
Some((0, Value::LightUserData(ud))) => {
ud.0 == &ASYNC_POLL_PENDING as *const u8 as *mut c_void
std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8)
}
_ => false,
}
+12 -15
View File
@@ -1,3 +1,4 @@
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex};
@@ -13,7 +14,7 @@ use crate::error::Result;
use crate::ffi;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::lua::Lua;
use crate::lua::{ExtraData, Lua};
use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue;
@@ -29,26 +30,22 @@ pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
pub(crate) struct CallbackUpvalue<'lua> {
pub(crate) lua: Lua,
pub(crate) func: Callback<'lua, 'static>,
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: Arc<UnsafeCell<ExtraData>>,
}
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
#[cfg(feature = "async")]
pub(crate) struct AsyncCallbackUpvalue<'lua> {
pub(crate) lua: Lua,
pub(crate) func: AsyncCallback<'lua, 'static>,
}
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
#[cfg(feature = "async")]
pub(crate) struct AsyncPollUpvalue<'lua> {
pub(crate) lua: Lua,
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
}
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<MultiValue<'static>>>>;
/// Type to set next Luau VM action after executing interrupt function.
#[cfg(any(feature = "luau", doc))]
@@ -65,10 +62,10 @@ pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
#[cfg(all(feature = "luau", feature = "send"))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState> + Send>;
#[cfg(all(feature = "luau", not(feature = "send")))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
@@ -86,7 +83,7 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
pub(crate) struct DestructedUserdataMT;
pub(crate) struct DestructedUserdata;
/// An auto generated key into the Lua registry.
///
+26 -12
View File
@@ -118,7 +118,17 @@ pub enum MetaMethod {
///
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
#[cfg(any(feature = "lua52", feature = "luajit52", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52"))))]
IPairs,
/// The `__iter` metamethod.
///
/// Executed before the iteration begins, and should return an iterator function like `next`
/// (or a custom one).
///
/// Requires `feature = "lua"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Iter,
/// The `__close` metamethod.
///
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
@@ -203,6 +213,8 @@ impl MetaMethod {
MetaMethod::Pairs => "__pairs",
#[cfg(any(feature = "lua52", feature = "luajit52"))]
MetaMethod::IPairs => "__ipairs",
#[cfg(feature = "luau")]
MetaMethod::Iter => "__iter",
#[cfg(feature = "lua54")]
MetaMethod::Close => "__close",
@@ -270,6 +282,8 @@ impl From<StdString> for MetaMethod {
"__pairs" => MetaMethod::Pairs,
#[cfg(any(feature = "lua52", feature = "luajit52"))]
"__ipairs" => MetaMethod::IPairs,
#[cfg(feature = "luau")]
"__iter" => MetaMethod::Iter,
#[cfg(feature = "lua54")]
"__close" => MetaMethod::Close,
@@ -914,16 +928,16 @@ impl<'lua> AnyUserData<'lua> {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(lua.state, 2, 0, |state| {
if getuservalue_table(lua.state, -2) != ffi::LUA_TTABLE {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(lua.state, 1);
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(lua.state, -4, USER_VALUE_MAXSLOT as c_int);
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(lua.state, -4);
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushvalue(state, -2);
#[cfg(feature = "lua54")]
@@ -967,8 +981,8 @@ impl<'lua> AnyUserData<'lua> {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(lua.state, 1, 1, |state| {
if getuservalue_table(lua.state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(lua.state);
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
#[cfg(feature = "lua54")]
@@ -1002,16 +1016,16 @@ impl<'lua> AnyUserData<'lua> {
// Multiple (extra) user values are emulated by storing them in a table
let name = name.as_ref();
protect_lua!(lua.state, 2, 0, |state| {
if getuservalue_table(lua.state, -2) != ffi::LUA_TTABLE {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(lua.state, 1);
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(lua.state, -4, USER_VALUE_MAXSLOT as c_int);
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(lua.state, -4);
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
ffi::lua_pushvalue(state, -3);
@@ -1040,8 +1054,8 @@ impl<'lua> AnyUserData<'lua> {
// Multiple (extra) user values are emulated by storing them in a table
let name = name.as_ref();
protect_lua!(lua.state, 1, 1, |state| {
if getuservalue_table(lua.state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(lua.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());
+46
View File
@@ -246,11 +246,23 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
@@ -301,12 +313,24 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<RwLock<T>>>(lua.state)?;
let mut ud =
ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
@@ -354,11 +378,24 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud =
get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
_ => Err(Error::UserDataTypeMismatch),
}
}
@@ -581,3 +618,12 @@ macro_rules! lua_userdata_impl {
lua_userdata_impl!(Rc<RefCell<T>>);
lua_userdata_impl!(Arc<Mutex<T>>);
lua_userdata_impl!(Arc<RwLock<T>>);
#[cfg(feature = "parking_lot")]
lua_userdata_impl!(Arc<parking_lot::Mutex<T>>);
#[cfg(feature = "parking_lot")]
lua_userdata_impl!(Arc<parking_lot::RwLock<T>>);
// A special proxy object for UserData
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
lua_userdata_impl!(UserDataProxy<T>);
+104 -52
View File
@@ -1,6 +1,7 @@
use std::any::{Any, TypeId};
use std::ffi::CStr;
use std::fmt::Write;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc;
@@ -136,26 +137,21 @@ where
F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
{
union URes<R: Copy> {
uninit: (),
init: R,
}
struct Params<F, R: Copy> {
function: F,
result: URes<R>,
result: MaybeUninit<R>,
nresults: c_int,
}
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where
R: Copy,
F: Fn(*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.init = ((*params).function)(state);
(*params).result.write(((*params).function)(state));
if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state)
@@ -174,7 +170,7 @@ where
let mut params = Params {
function: f,
result: URes { uninit: () },
result: MaybeUninit::uninit(),
nresults,
};
@@ -185,7 +181,7 @@ where
if ret == ffi::LUA_OK {
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
// `params.result` is definitely initialized.
Ok(params.result.init)
Ok(params.result.assume_init())
} else {
Err(pop_error(state, ret))
}
@@ -203,7 +199,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
"pop_error called with non-error return code"
);
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
Some(WrappedFailure::Error(err)) => {
ffi::lua_pop(state, 1);
err.clone()
@@ -251,17 +247,33 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
state: *mut ffi::lua_State,
s: &S,
protect: bool,
) -> Result<()> {
let s = s.as_ref();
protect_lua!(state, 0, 1, |state| {
if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
} else {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
Ok(())
}
}
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
pub unsafe fn push_table(
state: *mut ffi::lua_State,
narr: c_int,
nrec: c_int,
protect: bool,
) -> Result<()> {
if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
} else {
ffi::lua_createtable(state, narr, nrec);
Ok(())
}
}
// Uses 4 stack spaces, does not call checkstack.
@@ -281,10 +293,14 @@ where
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(not(feature = "luau"))]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua!(state, 0, 1, |state| {
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?
} else {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?;
};
ptr::write(ud, t);
Ok(())
}
@@ -292,20 +308,20 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "luau")]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
unsafe extern "C" fn destructor<T>(ud: *mut c_void) {
let ud = ud as *mut T;
if *(ud.offset(1) as *mut u8) == 0 {
ptr::drop_in_place(ud);
}
ptr::drop_in_place(ud as *mut T);
}
let ud = protect_lua!(state, 0, 1, |state| {
let size = mem::size_of::<T>() + 1;
let size = mem::size_of::<T>() + 1;
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
})?
} else {
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
})?;
};
ptr::write(ud, t);
*(ud.offset(1) as *mut u8) = 0; // Mark as not destructed
Ok(())
}
@@ -313,10 +329,19 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub unsafe fn push_userdata_uv<T>(state: *mut ffi::lua_State, t: T, nuvalue: c_int) -> Result<()> {
let ud = protect_lua!(state, 0, 1, |state| {
pub 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, mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
})?;
};
ptr::write(ud, t);
Ok(())
}
@@ -340,33 +365,51 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
get_destructed_userdata_metatable(state);
ffi::lua_setmetatable(state, -2);
let ud = get_userdata::<T>(state, -1);
// Update userdata tag to disable destructor and mark as destructed
#[cfg(feature = "luau")]
ffi::lua_setuserdatatag(state, -1, 1);
ffi::lua_pop(state, 1);
if cfg!(feature = "luau") {
*(ud.offset(1) as *mut u8) = 1; // Mark as destructed
}
ptr::read(ud)
}
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack.
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_userdata(state, t)?;
pub unsafe fn push_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
t: T,
protect: bool,
) -> Result<()> {
push_userdata(state, t, protect)?;
get_gc_metatable::<T>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
pub unsafe fn get_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
index: c_int,
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 {
return ptr::null_mut();
}
get_gc_metatable::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if res == 0 {
return ptr::null_mut();
if !mt_ptr.is_null() {
let ud_mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
if !ptr::eq(ud_mt_ptr, mt_ptr) {
return ptr::null_mut();
}
} else {
get_gc_metatable::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if res == 0 {
return ptr::null_mut();
}
}
ud
}
@@ -505,7 +548,7 @@ pub unsafe fn init_userdata_metatable<T>(
// Push `__index` generator function
init_userdata_metatable_index(state)?;
push_string(state, "__index")?;
push_string(state, "__index", true)?;
let index_type = ffi::lua_rawget(state, -3);
match index_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
@@ -530,7 +573,7 @@ pub unsafe fn init_userdata_metatable<T>(
// Push `__newindex` generator function
init_userdata_metatable_newindex(state)?;
push_string(state, "__newindex")?;
push_string(state, "__newindex", true)?;
let newindex_type = ffi::lua_rawget(state, -3);
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
@@ -642,7 +685,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
return 1;
}
if get_gc_userdata::<WrappedFailure>(state, -1).is_null() {
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, s, 0);
@@ -669,7 +712,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettop(state)
} else {
if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{
ffi::lua_error(state);
}
@@ -685,7 +728,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{
1
} else {
@@ -715,7 +758,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettop(state) - 1
} else {
if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{
ffi::lua_error(state);
}
@@ -758,7 +801,7 @@ pub unsafe fn init_gc_metatable<T: Any>(
) -> Result<()> {
check_stack(state, 6)?;
push_table(state, 0, 3)?;
push_table(state, 0, 3, true)?;
#[cfg(not(feature = "luau"))]
{
@@ -799,7 +842,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
callback_error(state, |_| {
check_stack(state, 3)?;
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1).as_ref() {
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
Some(WrappedFailure::Error(error)) => {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
@@ -836,7 +879,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
}
}?;
push_string(state, &*err_buf)?;
push_string(state, &*err_buf, true)?;
(*err_buf).clear();
Ok(1)
@@ -857,7 +900,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
callback_error(state, |_| Err(Error::CallbackDestructed))
}
push_table(state, 0, 26)?;
push_table(state, 0, 26, true)?;
ffi::lua_pushcfunction(state, destructed_error);
for &method in &[
"__add",
@@ -899,6 +942,8 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
"__pairs",
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
"__ipairs",
#[cfg(feature = "luau")]
"__iter",
#[cfg(feature = "lua54")]
"__close",
] {
@@ -914,7 +959,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create error print buffer
init_gc_metatable::<String>(state, None)?;
push_gc_userdata(state, String::new())?;
push_gc_userdata(state, String::new(), true)?;
protect_lua!(state, 1, 0, fn(state) {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
@@ -992,6 +1037,13 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
pub(crate) unsafe fn ptr_to_cstr_bytes<'a>(input: *const c_char) -> Option<&'a [u8]> {
if input.is_null() {
return None;
}
Some(CStr::from_ptr(input).to_bytes())
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
static USERDATA_METATABLE_INDEX: u8 = 0;
+56 -7
View File
@@ -1,5 +1,7 @@
use std::iter::{self, FromIterator};
use std::{slice, str, vec};
use std::ops::Index;
use std::os::raw::c_void;
use std::{ptr, slice, str, vec};
#[cfg(feature = "serialize")]
use {
@@ -9,6 +11,7 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
@@ -93,6 +96,30 @@ impl<'lua> Value<'lua> {
_ => Ok(self == other.as_ref()),
}
}
/// Converts the value to a generic C pointer.
///
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it returns NULL.
/// Different objects will give different pointers.
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
unsafe {
match self {
Value::LightUserData(ud) => ud.0,
Value::Table(t) => t.to_pointer(),
Value::String(s) => s.to_pointer(),
Value::Function(Function(v))
| Value::Thread(Thread(v))
| Value::UserData(AnyUserData(v)) => v
.lua
.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, v.index)),
_ => ptr::null(),
}
}
}
}
impl<'lua> PartialEq for Value<'lua> {
@@ -171,7 +198,7 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub fn new() -> MultiValue<'lua> {
pub const fn new() -> MultiValue<'lua> {
MultiValue(Vec::new())
}
@@ -212,7 +239,24 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.0).iter().rev()
self.0.iter().rev()
}
}
impl<'lua> Index<usize> for MultiValue<'lua> {
type Output = Value<'lua>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
if let Some(result) = self.get(index) {
result
} else {
panic!(
"index out of bounds: the len is {} but the index is {}",
self.len(),
index
)
}
}
}
@@ -230,19 +274,24 @@ impl<'lua> MultiValue<'lua> {
v
}
#[inline]
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
self.0.get(self.0.len() - index - 1)
}
#[inline]
pub(crate) fn reserve(&mut self, size: usize) {
self.0.reserve(size);
}
#[inline]
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
self.0.push(value);
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
self.0.pop()
}
#[inline]
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
self.0.pop()
pub fn push_front(&mut self, value: Value<'lua>) {
self.0.push(value);
}
#[inline]
+4 -1
View File
@@ -75,7 +75,10 @@ async fn test_async_call() -> Result<()> {
async fn test_async_bind_call() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move { Ok(a + b) })?;
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
tokio::task::yield_now().await;
Ok(a + b)
})?;
let plus_10 = sum.bind(10)?;
lua.globals().set("plus_10", plus_10)?;
+54
View File
@@ -0,0 +1,54 @@
use std::fs;
use std::io;
use mlua::{Error, Lua, Result};
#[test]
fn test_chunk_path() -> Result<()> {
let lua = Lua::new();
let temp_dir = tempfile::tempdir().unwrap();
fs::write(
temp_dir.path().join("module.lua"),
r#"
return 321
"#,
)?;
let i: i32 = lua.load(&temp_dir.path().join("module.lua")).eval()?;
assert_eq!(i, 321);
match lua.load(&temp_dir.path().join("module2.lua")).exec() {
Err(Error::ExternalError(err))
if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
res => panic!("expected io::Error, got {:?}", res),
};
Ok(())
}
#[test]
#[cfg(feature = "macros")]
fn test_chunk_macro() -> Result<()> {
let lua = Lua::new();
let name = "Rustacean";
let table = vec![1];
let data = lua.create_table()?;
data.raw_set("num", 1)?;
lua.globals().set("g", 123)?;
lua.load(mlua::chunk! {
assert($name == "Rustacean")
assert($table[1] == 1)
assert($data.num == 1)
assert(g == 123)
s = 321
})
.exec()?;
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
Ok(())
}
+39 -34
View File
@@ -1,36 +1,41 @@
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 9:10...
--> $DIR/async_nonstatic_userdata.rs:9:10
|
9 | impl<'a> UserData for MyUserData<'a> {
| ^^
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
--> tests/compile/async_nonstatic_userdata.rs:9:10
|
9 | impl<'a> UserData for MyUserData<'a> {
| ^^
note: ...so that the types are compatible
--> $DIR/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
= note: expected `(MyUserData<'_>,)`
found `(MyUserData<'a>,)`
note: but, the lifetime must be valid for the lifetime `'lua` as defined on the method body at 10:24...
--> $DIR/async_nonstatic_userdata.rs:10:24
|
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ^^^^
note: ...so that the type `impl Future` will meet its required lifetime bounds
--> $DIR/async_nonstatic_userdata.rs:11:21
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ^^^^^^^^^^^^^^^^
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ________________________________________________________________________^
12 | | println!("{}", data.0);
13 | | Ok(())
14 | | });
| |_____________^
= note: expected `(MyUserData<'_>,)`
found `(MyUserData<'a>,)`
note: but, the lifetime must be valid for the lifetime `'lua` as defined here...
--> tests/compile/async_nonstatic_userdata.rs:10:24
|
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ^^^^
note: ...so that the type `impl Future<Output = [async output]>` will meet its required lifetime bounds...
--> tests/compile/async_nonstatic_userdata.rs:11:21
|
11 | methods.add_async_method("print", |_, data, ()| async move {
| ^^^^^^^^^^^^^^^^
note: ...that is required by this bound
--> src/userdata.rs
|
| MR: 'lua + Future<Output = Result<R>>;
| ^^^^
+6 -23
View File
@@ -1,30 +1,13 @@
error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<()>`
= note: required because it appears within the type `PhantomData<UnsafeCell<()>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
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
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
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
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::ExtraData>>`
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
+18 -13
View File
@@ -1,14 +1,19 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> $DIR/non_send.rs:11:9
|
11 | lua.create_function(move |_, ()| {
| _________^^^^^^^^^^^^^^^_-
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
12 | | Ok(data.get())
13 | | })?
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
--> tests/compile/non_send.rs:11:9
|
11 | lua.create_function(move |_, ()| {
| _________^^^^^^^^^^^^^^^_-
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
12 | | Ok(data.get())
13 | | })?
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
|
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+6 -25
View File
@@ -1,32 +1,13 @@
error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:5
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^^^^^^ `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<()>`
= note: required because it appears within the type `PhantomData<UnsafeCell<()>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
= note: required because it appears within the type `LuaTable<'_>`
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
note: required by a bound in `catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
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
--> tests/compile/ref_nounwindsafe.rs:8:5
|
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
|
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::ExtraData>>`
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
= note: required because it appears within the type `Lua`
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
+1 -1
View File
@@ -19,7 +19,7 @@ error[E0373]: closure may outlive the current function, but it borrows `inner`,
--> tests/compile/scope_callback_inner.rs:8:34
|
5 | lua.scope(|scope| {
| ----- has type `&Scope<'_, '2>`
| ----- has type `&mlua::Scope<'_, '2>`
...
8 | .create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
+1 -1
View File
@@ -2,7 +2,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
--> tests/compile/scope_invariance.rs:14:38
|
9 | lua.scope(|scope| {
| ----- has type `&Scope<'_, '1>`
| ----- has type `&mlua::Scope<'_, '1>`
...
14 | .create_function_mut(|_, ()| {
| ^^^^^^^ may outlive borrowed value `test`
+2 -2
View File
@@ -1,8 +1,8 @@
error[E0597]: `ibad` does not live long enough
--> $DIR/scope_userdata_borrow.rs:15:56
--> tests/compile/scope_userdata_borrow.rs:15:56
|
11 | lua.scope(|scope| {
| ----- has type `&Scope<'_, '1>`
| ----- has type `&mlua::Scope<'_, '1>`
...
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
| -------------------------------------------^^^^^--
+12 -14
View File
@@ -1,35 +1,33 @@
error[E0597]: `lua` does not live long enough
--> $DIR/static_callback_args.rs:12:5
--> tests/compile/static_callback_args.rs:12:5
|
12 | lua.create_function(|_, table: Table| {
| -^^
| |
| _____borrowed value does not live long enough
| |
12 | / lua.create_function(|_, table: Table| {
13 | | BAD_TIME.with(|bt| {
14 | | *bt.borrow_mut() = Some(table);
15 | | });
16 | | Ok(())
17 | | })?
| |______- argument requires that `lua` is borrowed for `'static`
| | ^
| | |
| |______borrowed value does not live long enough
| 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
--> $DIR/static_callback_args.rs:22:10
--> tests/compile/static_callback_args.rs:22:10
|
12 | lua.create_function(|_, table: Table| {
| ---
| |
| _____borrow of `lua` occurs here
| |
12 | / lua.create_function(|_, table: Table| {
13 | | BAD_TIME.with(|bt| {
14 | | *bt.borrow_mut() = Some(table);
15 | | });
16 | | Ok(())
17 | | })?
| |______- argument requires that `lua` is borrowed for `'static`
| | -
| | |
| |______borrow of `lua` occurs here
| argument requires that `lua` is borrowed for `'static`
...
22 | drop(lua);
| ^^^ move out of `lua` occurs here
+60
View File
@@ -42,11 +42,17 @@ fn test_bind() -> Result<()> {
concat = concat.bind("foo")?;
concat = concat.bind("bar")?;
concat = concat.bind(("baz", "baf"))?;
assert_eq!(concat.call::<_, String>(())?, "foobarbazbaf");
assert_eq!(
concat.call::<_, String>(("hi", "wut"))?,
"foobarbazbafhiwut"
);
let mut concat2 = globals.get::<_, Function>("concat")?;
concat2 = concat2.bind(())?;
assert_eq!(concat2.call::<_, String>(())?, "");
assert_eq!(concat2.call::<_, String>(("ab", "cd"))?, "abcd");
Ok(())
}
@@ -107,3 +113,57 @@ fn test_dump() -> Result<()> {
Ok(())
}
#[test]
fn test_function_info() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
lua.load(
r#"
function function1()
return function() end
end
"#,
)
.set_name("source1")?
.exec()?;
let function1 = globals.get::<_, Function>("function1")?;
let function2 = function1.call::<_, Function>(())?;
let function3 = lua.create_function(|_, ()| Ok(()))?;
let function1_info = function1.info();
#[cfg(feature = "luau")]
assert_eq!(function1_info.name, Some(b"function1".to_vec()));
assert_eq!(function1_info.source, Some(b"source1".to_vec()));
assert_eq!(function1_info.line_defined, 2);
#[cfg(not(feature = "luau"))]
assert_eq!(function1_info.last_line_defined, 4);
assert_eq!(function1_info.what, Some(b"Lua".to_vec()));
let function2_info = function2.info();
assert_eq!(function2_info.name, None);
assert_eq!(function2_info.source, Some(b"source1".to_vec()));
assert_eq!(function2_info.line_defined, 3);
#[cfg(not(feature = "luau"))]
assert_eq!(function2_info.last_line_defined, 3);
assert_eq!(function2_info.what, Some(b"Lua".to_vec()));
let function3_info = function3.info();
assert_eq!(function3_info.name, None);
assert_eq!(function3_info.source, Some(b"=[C]".to_vec()));
assert_eq!(function3_info.line_defined, -1);
#[cfg(not(feature = "luau"))]
assert_eq!(function3_info.last_line_defined, -1);
assert_eq!(function3_info.what, Some(b"C".to_vec()));
let print_info = globals.get::<_, Function>("print")?.info();
#[cfg(feature = "luau")]
assert_eq!(print_info.name, Some(b"print".to_vec()));
assert_eq!(print_info.source, Some(b"=[C]".to_vec()));
assert_eq!(print_info.what, Some(b"C".to_vec()));
assert_eq!(print_info.line_defined, -1);
Ok(())
}
+88 -9
View File
@@ -5,7 +5,7 @@ use std::fs;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use mlua::{Error, Lua, Result, Table, ThreadStatus, Value, VmState};
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
#[test]
fn test_require() -> Result<()> {
@@ -15,8 +15,11 @@ fn test_require() -> Result<()> {
fs::write(
temp_dir.path().join("module.luau"),
r#"
counter = counter or 0
return counter + 1
counter = (counter or 0) + 1
return {
counter = counter,
error = function() error("test") end,
}
"#,
)?;
@@ -24,9 +27,12 @@ fn test_require() -> Result<()> {
lua.load(
r#"
local module = require("module")
assert(module == 1)
assert(module.counter == 1)
module = require("module")
assert(module == 1)
assert(module.counter == 1)
local ok, err = pcall(module.error)
assert(not ok and string.find(err, "module.luau") ~= nil)
"#,
)
.exec()
@@ -59,7 +65,7 @@ fn test_vectors() -> Result<()> {
assert(v.z == 3)
"#,
)
.set_vector_ctor(Some("vector".to_string()))
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
.exec()?;
Ok(())
@@ -150,7 +156,7 @@ fn test_interrupts() -> Result<()> {
let interrupts_count = Arc::new(AtomicU64::new(0));
let interrupts_count2 = interrupts_count.clone();
lua.set_interrupt(move |_lua| {
lua.set_interrupt(move || {
interrupts_count2.fetch_add(1, Ordering::Relaxed);
Ok(VmState::Continue)
});
@@ -172,7 +178,7 @@ fn test_interrupts() -> Result<()> {
//
let yield_count = Arc::new(AtomicU64::new(0));
let yield_count2 = yield_count.clone();
lua.set_interrupt(move |_lua| {
lua.set_interrupt(move || {
if yield_count2.fetch_add(1, Ordering::Relaxed) == 1 {
return Ok(VmState::Yield);
}
@@ -199,7 +205,7 @@ fn test_interrupts() -> Result<()> {
//
// Test errors in interrupts
//
lua.set_interrupt(|_| Err(Error::RuntimeError("error from interrupt".into())));
lua.set_interrupt(|| Err(Error::RuntimeError("error from interrupt".into())));
match f.call::<_, ()>(()) {
Err(Error::CallbackError { cause, .. }) => match *cause {
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
@@ -212,3 +218,76 @@ fn test_interrupts() -> Result<()> {
Ok(())
}
#[test]
fn test_coverate() -> Result<()> {
let lua = Lua::new();
lua.set_compiler(Compiler::default().set_coverage_level(1));
let f = lua
.load(
r#"local v = vector(1, 2, 3)
assert(v.x == 1 and v.y == 2 and v.z == 3)
function abc(i)
if i < 5 then
return 0
else
return 1
end
end
(function()
(function() abc(10) end)()
end)()
"#,
)
.into_function()?;
f.call(())?;
let mut report = Vec::new();
f.coverage(|cov| {
report.push(cov);
});
assert_eq!(
report[0],
CoverageInfo {
function: None,
line_defined: 1,
depth: 0,
hits: vec![-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1],
}
);
assert_eq!(
report[1],
CoverageInfo {
function: Some("abc".into()),
line_defined: 4,
depth: 1,
hits: vec![-1, -1, -1, -1, -1, 1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1],
}
);
assert_eq!(
report[2],
CoverageInfo {
function: None,
line_defined: 12,
depth: 1,
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
}
);
assert_eq!(
report[3],
CoverageInfo {
function: None,
line_defined: 13,
depth: 2,
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
}
);
Ok(())
}
-29
View File
@@ -1,29 +0,0 @@
#![cfg(feature = "macros")]
use mlua::{chunk, Lua, Result};
#[test]
fn test_chunk_macro() -> Result<()> {
let lua = Lua::new();
let name = "Rustacean";
let table = vec![1];
let data = lua.create_table()?;
data.raw_set("num", 1)?;
lua.globals().set("g", 123)?;
lua.load(chunk! {
assert($name == "Rustacean")
assert($table[1] == 1)
assert($data.num == 1)
assert(g == 123)
s = 321
})
.exec()?;
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
Ok(())
}
+7 -5
View File
@@ -1,6 +1,6 @@
use std::sync::Arc;
use mlua::{Lua, Result, UserData};
use mlua::{GCMode, Lua, Result, UserData};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use mlua::Error;
@@ -39,7 +39,10 @@ fn test_gc_control() -> Result<()> {
let globals = lua.globals();
#[cfg(feature = "lua54")]
assert_eq!(lua.gc_gen(0, 0), mlua::GCMode::Incremental);
{
assert_eq!(lua.gc_gen(0, 0), GCMode::Incremental);
assert_eq!(lua.gc_inc(0, 0, 0), GCMode::Generational);
}
#[cfg(any(
feature = "lua54",
@@ -55,6 +58,8 @@ fn test_gc_control() -> Result<()> {
assert!(lua.gc_is_running());
}
assert_eq!(lua.gc_inc(200, 100, 13), GCMode::Incremental);
struct MyUserdata(Arc<()>);
impl UserData for MyUserdata {}
@@ -67,9 +72,6 @@ fn test_gc_control() -> Result<()> {
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&rc), 1);
#[cfg(feature = "lua54")]
assert_eq!(lua.gc_inc(0, 0, 0), mlua::GCMode::Generational);
Ok(())
}
+8 -5
View File
@@ -1,8 +1,11 @@
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
+10 -3
View File
@@ -1,16 +1,23 @@
[package]
name = "module_runner"
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[workspace]
members = [
"loader",
]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../.." }
mlua = { path = "../..", features = ["module"] }
+8
View File
@@ -0,0 +1,8 @@
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "module_loader"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../../.." }
+39
View File
@@ -0,0 +1,39 @@
use mlua::prelude::*;
fn sum(_: &Lua, (a, b): (i64, i64)) -> LuaResult<i64> {
Ok(a + b)
}
fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
Ok(ud.0)
}
#[mlua::lua_module]
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
Ok(exports)
}
#[derive(Clone, Copy)]
struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module]
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
Ok(exports)
}
#[mlua::lua_module]
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".to_lua_err())
}
+13
View File
@@ -375,6 +375,19 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[test]
fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
#[derive(Deserialize, PartialEq, Debug)]
struct Test(f64);
let got = lua.from_value(Value::Number(123.456))?;
assert_eq!(Test(123.456), got);
Ok(())
}
#[test]
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
+114
View File
@@ -0,0 +1,114 @@
use std::cell::RefCell;
use mlua::{Lua, Result, Table};
#[test]
fn test_static_lua() -> Result<()> {
let lua = Lua::new().into_static();
thread_local! {
static TABLE: RefCell<Option<Table<'static>>> = RefCell::new(None);
}
let f = lua.create_function(|_, table: Table| {
TABLE.with(|t| {
table.raw_insert(1, "hello")?;
*t.borrow_mut() = Some(table);
Ok(())
})
})?;
f.call(lua.create_table()?)?;
drop(f);
lua.gc_collect()?;
TABLE.with(|t| {
assert!(t.borrow().as_ref().unwrap().len().unwrap() == 1);
*t.borrow_mut() = None;
});
// Consume the Lua instance
unsafe { Lua::from_static(lua) };
Ok(())
}
#[test]
fn test_static_lua_coroutine() -> Result<()> {
let lua = Lua::new().into_static();
thread_local! {
static TABLE: RefCell<Option<Table<'static>>> = RefCell::new(None);
}
let f = lua.create_function(|_, table: Table| {
TABLE.with(|t| {
table.raw_insert(1, "hello")?;
*t.borrow_mut() = Some(table);
Ok(())
})
})?;
let co = lua.create_thread(f)?;
co.resume::<_, ()>(lua.create_table()?)?;
drop(co);
lua.gc_collect()?;
TABLE.with(|t| {
assert_eq!(
t.borrow().as_ref().unwrap().get::<_, String>(1i32).unwrap(),
"hello".to_string()
);
*t.borrow_mut() = None;
});
// Consume the Lua instance
unsafe { Lua::from_static(lua) };
Ok(())
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_static_async() -> Result<()> {
let lua = Lua::new().into_static();
let timer =
lua.create_async_function(|_, (i, n, f): (u64, u64, mlua::Function)| async move {
tokio::task::spawn_local(async move {
let dur = std::time::Duration::from_millis(i);
for _ in 0..n {
tokio::task::spawn_local(f.call_async::<(), ()>(()));
tokio::time::sleep(dur).await;
}
});
Ok(())
})?;
lua.globals().set("timer", timer)?;
{
let local_set = tokio::task::LocalSet::new();
local_set
.run_until(
lua.load(
r#"
local cnt = 0
timer(1, 100, function()
cnt = cnt + 1
if cnt % 10 == 0 then
collectgarbage()
end
end)
"#,
)
.exec_async(),
)
.await?;
local_set.await;
}
// Consume the Lua instance
unsafe { Lua::from_static(lua) };
Ok(())
}
+16
View File
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::HashSet;
use mlua::{Lua, Result, String};
@@ -67,3 +68,18 @@ fn test_raw_string() -> Result<()> {
Ok(())
}
#[test]
fn test_string_hash() -> Result<()> {
let lua = Lua::new();
let set: HashSet<String> = lua.load(r#"{"hello", "world", "abc", 321}"#).eval()?;
assert_eq!(set.len(), 4);
assert!(set.contains(b"hello".as_ref()));
assert!(set.contains(b"world".as_ref()));
assert!(set.contains(b"abc".as_ref()));
assert!(set.contains(b"321".as_ref()));
assert!(!set.contains(b"Hello".as_ref()));
Ok(())
}
+17 -1
View File
@@ -560,7 +560,12 @@ fn test_num_conversion() -> Result<()> {
assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0);
#[cfg(any(feature = "lua54", feature = "lua53"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1.0");
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[cfg(any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1");
assert_eq!(lua.load("1.5").eval::<i64>()?, 1);
@@ -1272,3 +1277,14 @@ fn test_luajit_cdata() {
)
.eval();
}
#[test]
#[cfg(feature = "send")]
fn test_send() {
let lua = Lua::new();
std::thread::spawn(move || {
let _lua = lua;
})
.join()
.unwrap();
}
+53 -1
View File
@@ -1,4 +1,9 @@
use std::sync::{Arc, Mutex, RwLock};
use std::sync::Arc;
#[cfg(not(feature = "parking_lot"))]
use std::sync::{Mutex, RwLock};
#[cfg(feature = "parking_lot")]
use parking_lot::{Mutex, RwLock};
#[cfg(not(feature = "send"))]
use std::{cell::RefCell, rc::Rc};
@@ -623,7 +628,10 @@ fn test_userdata_wrapped() -> Result<()> {
"#,
)
.exec()?;
#[cfg(not(feature = "parking_lot"))]
assert_eq!(ud2.lock().unwrap().0, 3);
#[cfg(feature = "parking_lot")]
assert_eq!(ud2.lock().0, 3);
let ud3 = Arc::new(RwLock::new(MyUserData(3)));
globals.set("arc_rwlock_ud", ud3.clone())?;
@@ -634,7 +642,10 @@ fn test_userdata_wrapped() -> Result<()> {
"#,
)
.exec()?;
#[cfg(not(feature = "parking_lot"))]
assert_eq!(ud3.read().unwrap().0, 4);
#[cfg(feature = "parking_lot")]
assert_eq!(ud3.read().0, 4);
// Test drop
globals.set("arc_mutex_ud", Nil)?;
@@ -645,3 +656,44 @@ fn test_userdata_wrapped() -> Result<()> {
Ok(())
}
#[test]
fn test_userdata_proxy() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_function_get("static_field", |_, _| Ok(123));
fields.add_field_method_get("n", |_, this| Ok(this.0));
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("new", |_, n| Ok(Self(n)));
methods.add_method("plus", |_, this, n: i64| Ok(this.0 + n));
}
}
let lua = Lua::new();
let globals = lua.globals();
globals.set("MyUserData", lua.create_proxy::<MyUserData>()?)?;
lua.load(
r#"
assert(MyUserData.static_field == 123)
local data = MyUserData.new(321)
assert(data.static_field == 123)
assert(data.n == 321)
assert(data:plus(1) == 322)
-- Error when accessing the proxy object fields and methods that require instance
local ok = pcall(function() return MyUserData.n end)
assert(not ok)
ok = pcall(function() return MyUserData:plus(1) end)
assert(not ok)
"#,
)
.exec()
}
+12 -4
View File
@@ -1,3 +1,5 @@
use std::ptr;
use mlua::{Lua, Result, Value};
#[test]
@@ -41,17 +43,23 @@ fn test_value_eq() -> Result<()> {
let thread2: Value = globals.get("thread2")?;
assert!(table1 != table2);
assert!(table1.equals(table2)?);
assert!(table1.equals(&table2)?);
assert!(string1 == string2);
assert!(string1.equals(string2)?);
assert!(string1.equals(&string2)?);
assert!(num1 == num2);
assert!(num1.equals(num2)?);
assert!(num1 != num3);
assert!(func1 == func2);
assert!(func1 != func3);
assert!(!func1.equals(func3)?);
assert!(!func1.equals(&func3)?);
assert!(thread1 == thread2);
assert!(thread1.equals(thread2)?);
assert!(thread1.equals(&thread2)?);
assert!(!table1.to_pointer().is_null());
assert!(!ptr::eq(table1.to_pointer(), table2.to_pointer()));
assert!(ptr::eq(string1.to_pointer(), string2.to_pointer()));
assert!(ptr::eq(func1.to_pointer(), func2.to_pointer()));
assert!(num1.to_pointer().is_null());
Ok(())
}