mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
94 Commits
v0.6.0-beta.1
...
v0.6.4
| Author | SHA1 | Date | |
|---|---|---|---|
| 584b377640 | |||
| 1141073a65 | |||
| 5b1483bd56 | |||
| a74b637ed4 | |||
| 7623016d4a | |||
| bdd3c923ba | |||
| d586eef0f5 | |||
| 01154c0616 | |||
| e42d67c70d | |||
| 771a7775c5 | |||
| ee1c8a1a3d | |||
| 3597e34ffb | |||
| 35eedd5a5e | |||
| 53f873a482 | |||
| fc1fe2c15e | |||
| 7e7a44f4cd | |||
| 63c4861520 | |||
| 27e7facf9b | |||
| 31d32f2dda | |||
| 7d1b322e18 | |||
| d906405818 | |||
| 60fd060d47 | |||
| 9f02a9ca09 | |||
| 1d7f105585 | |||
| 1020315a9b | |||
| 628b70f561 | |||
| 8a7e546c66 | |||
| 97bd288f56 | |||
| 59253410bb | |||
| 5fb7b96704 | |||
| 17162b1e0b | |||
| 934aa3b65f | |||
| 3bffe1d7c3 | |||
| 582b2c585f | |||
| 56b6b128b5 | |||
| 1731f5d61b | |||
| 60822d12d2 | |||
| 8ff610529b | |||
| adbc9ccc9b | |||
| 84fe5f7f76 | |||
| 1fe583027b | |||
| 7b5b78fa3d | |||
| 41aae83304 | |||
| fc84e8623e | |||
| 8aae9a7c3e | |||
| 24bbd62564 | |||
| 30af919931 | |||
| 89b5981c59 | |||
| 7feabffc37 | |||
| f24fdfdb80 | |||
| c702c5eff2 | |||
| e4daff8c16 | |||
| 8d474bbf8d | |||
| 9e3b0ecc1e | |||
| 42b396d0d1 | |||
| 14d5c2c887 | |||
| b84c10fde6 | |||
| b49f9539b6 | |||
| a208156ed2 | |||
| 242bdafa75 | |||
| b84d1bd65f | |||
| 4e92ea341b | |||
| bf286751fa | |||
| 3b94b4e86f | |||
| 9f0378b77e | |||
| d3f44354e0 | |||
| fca21d56d3 | |||
| 6e52bb7e65 | |||
| 08ffeb0ca9 | |||
| 3b9d8a7b5f | |||
| 0fe898c0dd | |||
| 821f1125b6 | |||
| 6c0096d8ac | |||
| abb95c3c56 | |||
| 64faebf407 | |||
| a944f4ad6f | |||
| bae424672a | |||
| 6f9eb82649 | |||
| e8de2a458a | |||
| 973b5c3bf5 | |||
| b610a79d66 | |||
| fe39ae09bf | |||
| 01714d2510 | |||
| 0bad4a0ff9 | |||
| 35b7504076 | |||
| c9b8eb5418 | |||
| ef7d123f80 | |||
| 20cba5de5b | |||
| 3e03f4201c | |||
| 5199b02346 | |||
| 5293b8d6d2 | |||
| 7541b6f3f3 | |||
| 205510a540 | |||
| 2250421438 |
@@ -0,0 +1 @@
|
||||
github: khvzak
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize --out xml --exclude-files benches --exclude-files tests --exclude-files build --exclude-files src/ffi
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
+46
-17
@@ -24,17 +24,18 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --release --features "${{ matrix.lua }}"
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
@@ -47,11 +48,11 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -64,7 +65,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
@@ -73,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 }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -87,7 +88,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
@@ -96,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 }} async send serialize vendored"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -122,16 +123,43 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
name: Test with address sanitizer
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
@@ -141,7 +169,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest]
|
||||
rust: [stable, nightly]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
@@ -155,10 +183,11 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }} vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }} vendored")
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
|
||||
test_modules_windows:
|
||||
@@ -210,4 +239,4 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
@@ -1,3 +1,63 @@
|
||||
## v0.6.4
|
||||
|
||||
- Performance optimizations
|
||||
- Fixed table traversal used in recursion detection in deserializer
|
||||
|
||||
## v0.6.3
|
||||
|
||||
- Disabled catching Rust panics in userdata finalizers on drop. It also has positive performance impact.
|
||||
- Added `Debug::event()` to the hook's Debug structure
|
||||
- Simplified interface of `hook::HookTriggers`
|
||||
- Added finalizer to `ExtraData` in module mode. This helps avoiding memory leak on closing state when Lua unloads modules and frees memory.
|
||||
- Added `DeserializeOptions` struct to control deserializer behavior (`from_value_with` function).
|
||||
|
||||
## v0.6.2
|
||||
|
||||
- New functionality: `Lua::load_from_function()` and `Lua::create_c_function()`
|
||||
- Many optimizations in callbacks/userdata creation and methods execution
|
||||
|
||||
## v0.6.1
|
||||
|
||||
- Update `chunk!` documentation (stable Rust limitations)
|
||||
- Fixed Lua sequence table conversion to HashSet/BTreeSet
|
||||
- `once_cell` dependency lowered to 1.0
|
||||
|
||||
## v0.6.0
|
||||
Changes since 0.5.4
|
||||
- New `UserDataFields` API
|
||||
- Full access to `UserData` metatables with support of setting arbitrary fields.
|
||||
- Implement `UserData` for `Rc<RefCell<T>>`/`Arc<Mutex<T>>`/`Arc<RwLock<T>>` where `T: UserData`.
|
||||
- Added `SerializeOptions` to to change default Lua serializer behaviour (eg. `nil/null/array` serialization)
|
||||
- Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
|
||||
- Added `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`.
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads (coroutines) with attaching a new function.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
|
||||
- Improved errors reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in a module mode.
|
||||
- Added `String::to_string_lossy`
|
||||
- Various bugfixes and improvements
|
||||
|
||||
Breaking changes:
|
||||
- Errors are always `Send + Sync` to be compatible with the anyhow crate.
|
||||
- Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
|
||||
- Removed `Result` from `Function::dump()` (never fails)
|
||||
- Removed `AnyUserData::has_metamethod()` (in favour of full access to metatables)
|
||||
|
||||
## v0.6.0-beta.3
|
||||
|
||||
- Errors are always `Send + Sync` to be compatible with anyhow crate
|
||||
- Implement `UserData` for `Rc<RefCell>`/`Arc<Mutex>`/`Arc<RwLock>`
|
||||
- Added `__ipairs` metamethod for Lua 5.2
|
||||
- Added `String::to_string_lossy`
|
||||
- Various bugfixes and improvements
|
||||
|
||||
## v0.6.0-beta.2
|
||||
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
|
||||
- New `UserDataFields` API
|
||||
|
||||
+16
-7
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.6.0-beta.1" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.4" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -17,7 +17,7 @@ with async/await features and support of writing native lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua53", "async", "send", "serialize"]
|
||||
features = ["lua53", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -38,11 +38,12 @@ module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "0.5", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.7" }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
@@ -75,11 +76,11 @@ required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async"]
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_reqwest"
|
||||
required-features = ["async", "serialize"]
|
||||
required-features = ["async", "serialize", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
@@ -87,8 +88,16 @@ required-features = ["async", "send"]
|
||||
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
required-features = ["async"]
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "guided_tour"
|
||||
required-features = ["macros"]
|
||||
|
||||
[[example]]
|
||||
name = "serialize"
|
||||
required-features = ["serialize"]
|
||||
|
||||
[[example]]
|
||||
name = "userdata"
|
||||
required-features = ["macros"]
|
||||
|
||||
@@ -17,7 +17,7 @@ _safe_ (as far as it's possible), high level, easy to use, practical and flexibl
|
||||
|
||||
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targes are also supported).
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
|
||||
@@ -25,7 +25,7 @@ Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` sup
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of depenendies, compiled code and allow to choose only required set of features.
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: activate Lua [5.4] support
|
||||
@@ -38,6 +38,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
@@ -73,7 +74,7 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
### Compiling
|
||||
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
@@ -85,7 +86,7 @@ An example how to use them:
|
||||
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
```
|
||||
|
||||
`mlua` also supports vendored lua/luajit using the auxilary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
[luajit-src](https://crates.io/crates/luajit-src).
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
@@ -96,7 +97,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored"] }
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -131,7 +132,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored", "module"] }
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -184,6 +185,15 @@ MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
|
||||
|
||||
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
|
||||
|
||||
### Publishing to luarocks.org
|
||||
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
|
||||
Modules written in Rust and published to luarocks:
|
||||
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
|
||||
|
||||
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
|
||||
|
||||
## Safety
|
||||
|
||||
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
||||
@@ -233,7 +243,7 @@ If you encounter them, a bug report would be very welcome:
|
||||
|
||||
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
|
||||
|
||||
+ Lua C API errors are handled by lonjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
+ Lua C API errors are handled by longjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
|
||||
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
|
||||
|
||||
|
||||
@@ -59,6 +59,22 @@ fn create_string_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [function] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for i in 0..10 {
|
||||
lua.create_function(move |_, ()| Ok(i)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -258,6 +274,7 @@ criterion_group! {
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
|
||||
@@ -22,12 +22,12 @@ pub fn probe_lua() -> PathBuf {
|
||||
|
||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||
|
||||
if include_dir != "" {
|
||||
if !include_dir.is_empty() {
|
||||
if need_lua_lib {
|
||||
if lib_dir == "" {
|
||||
if lib_dir.is_empty() {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib == "" {
|
||||
if lua_lib.is_empty() {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
}
|
||||
|
||||
|
||||
@@ -239,14 +239,5 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
|
||||
}
|
||||
|
||||
let mut shim_cc = cc::Build::new();
|
||||
shim_cc
|
||||
.include(include_dir)
|
||||
.define("COMPAT53_INCLUDE_SOURCE", None);
|
||||
#[cfg(feature = "luajit")]
|
||||
shim_cc.define("COMPAT53_LUAJIT", None);
|
||||
shim_cc.file("src/ffi/shim/shim.c").compile("shim");
|
||||
|
||||
println!("cargo:rerun-if-changed=src/ffi/shim");
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
only_pulls: true
|
||||
project:
|
||||
default:
|
||||
only_pulls: true
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::BString;
|
||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||
use hyper::Client as HyperClient;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BodyReader(Arc<Mutex<HyperBody>>);
|
||||
@@ -19,11 +18,11 @@ impl BodyReader {
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("read", |_, reader, ()| async move {
|
||||
methods.add_async_method("read", |lua, reader, ()| async move {
|
||||
let mut reader = reader.0.lock().await;
|
||||
if let Some(bytes) = reader.data().await {
|
||||
let bytes = bytes.map_err(Error::external)?;
|
||||
return Ok(Some(BString::from(bytes.as_ref())));
|
||||
let bytes = bytes.to_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
@@ -36,18 +35,18 @@ async fn main() -> Result<()> {
|
||||
|
||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||
let client = HyperClient::new();
|
||||
let uri = uri.parse().map_err(Error::external)?;
|
||||
let resp = client.get(uri).await.map_err(Error::external)?;
|
||||
let uri = uri.parse().to_lua_err()?;
|
||||
let resp = client.get(uri).await.to_lua_err()?;
|
||||
|
||||
let lua_resp = lua.create_table()?;
|
||||
lua_resp.set("status", resp.status().as_u16())?;
|
||||
|
||||
let mut headers = HashMap::new();
|
||||
for (key, value) in resp.headers().iter() {
|
||||
for (key, value) in resp.headers() {
|
||||
headers
|
||||
.entry(key.as_str())
|
||||
.or_insert(Vec::new())
|
||||
.push(value.to_str().unwrap());
|
||||
.push(value.to_str().to_lua_err()?);
|
||||
}
|
||||
|
||||
lua_resp.set("headers", headers)?;
|
||||
@@ -56,13 +55,9 @@ async fn main() -> Result<()> {
|
||||
Ok(lua_resp)
|
||||
})?;
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("fetch_url", fetch_url)?;
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
local res = fetch_url(...)
|
||||
.load(chunk! {
|
||||
local res = $fetch_url(...)
|
||||
print(res.status)
|
||||
for key, vals in pairs(res.headers) do
|
||||
for _, val in ipairs(vals) do
|
||||
@@ -75,8 +70,7 @@ async fn main() -> Result<()> {
|
||||
print(body)
|
||||
end
|
||||
until not body
|
||||
"#,
|
||||
)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
f.call_async("http://httpbin.org/ip").await
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result};
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let null = lua.null();
|
||||
|
||||
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||
let resp = reqwest::get(&uri)
|
||||
.await
|
||||
.and_then(|resp| resp.error_for_status())
|
||||
.map_err(Error::external)?;
|
||||
let json = resp
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.map_err(Error::external)?;
|
||||
.to_lua_err()?;
|
||||
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
globals.set("fetch_json", fetch_json)?;
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
.load(chunk! {
|
||||
function print_r(t, indent)
|
||||
local indent = indent or ''
|
||||
local indent = indent or ""
|
||||
for k, v in pairs(t) do
|
||||
io.write(indent, tostring(k))
|
||||
if type(v) == "table" then io.write(':\n') print_r(v, indent..' ')
|
||||
else io.write(': ', v == null and "null" or tostring(v), '\n') end
|
||||
if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
|
||||
else io.write(": ", v == $null and "null" or tostring(v), "\n") end
|
||||
end
|
||||
end
|
||||
|
||||
local res = fetch_json(...)
|
||||
local res = $fetch_json(...)
|
||||
print_r(res)
|
||||
"#,
|
||||
)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
f.call_async("https://httpbin.org/anything?arg0=val0").await
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
@@ -7,13 +6,12 @@ use hyper::{Body, Request, Response, Server};
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaRequest(Arc<(SocketAddr, Request<Body>)>);
|
||||
struct LuaRequest(SocketAddr, Request<Body>);
|
||||
|
||||
impl UserData for LuaRequest {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).0.to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.0).1.method().to_string()));
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +23,7 @@ async fn run_server(handler: Function<'static>) -> Result<()> {
|
||||
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
|
||||
let handler = handler.clone();
|
||||
async move {
|
||||
let lua_req = LuaRequest(Arc::new((remote_addr, req)));
|
||||
let lua_req = LuaRequest(remote_addr, req);
|
||||
let lua_resp: Table = handler.call_async(lua_req).await?;
|
||||
let body = lua_resp
|
||||
.get::<_, Option<String>>("body")?
|
||||
@@ -72,7 +70,7 @@ async fn main() -> Result<()> {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello, World!"
|
||||
body = "Hello, World!\n"
|
||||
}
|
||||
end
|
||||
"#,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::BString;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task;
|
||||
|
||||
use mlua::{Function, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{chunk, Function, Lua, Result, String as LuaString, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaTcp;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -41,15 +39,15 @@ impl UserData for LuaTcpStream {
|
||||
Ok(stream.0.lock().await.peer_addr()?.to_string())
|
||||
});
|
||||
|
||||
methods.add_async_method("read", |_, stream, size: usize| async move {
|
||||
methods.add_async_method("read", |lua, stream, size: usize| async move {
|
||||
let mut buf = vec![0; size];
|
||||
let n = stream.0.lock().await.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
Ok(BString::from(buf))
|
||||
lua.create_string(&buf)
|
||||
});
|
||||
|
||||
methods.add_async_method("write", |_, stream, data: BString| async move {
|
||||
let n = stream.0.lock().await.write(&data).await?;
|
||||
methods.add_async_method("write", |_, stream, data: LuaString| async move {
|
||||
let n = stream.0.lock().await.write(&data.as_bytes()).await?;
|
||||
Ok(n)
|
||||
});
|
||||
|
||||
@@ -66,15 +64,12 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("tcp", LuaTcp)?;
|
||||
globals.set("spawn", spawn)?;
|
||||
let tcp = LuaTcp;
|
||||
|
||||
let server = lua
|
||||
.load(
|
||||
r#"
|
||||
.load(chunk! {
|
||||
local addr = ...
|
||||
local listener = tcp.bind(addr)
|
||||
local listener = $tcp.bind(addr)
|
||||
print("listening on "..addr)
|
||||
|
||||
local accept_new = true
|
||||
@@ -87,7 +82,7 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
return
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
$spawn(function()
|
||||
while true do
|
||||
local data = stream:read(100)
|
||||
data = data:match("^%s*(.-)%s*$") -- trim
|
||||
@@ -106,8 +101,7 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
end
|
||||
end)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
task::LocalSet::new()
|
||||
|
||||
+24
-13
@@ -1,15 +1,15 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
|
||||
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
// *without* the debug library.
|
||||
let lua = Lua::new();
|
||||
|
||||
// You can get and set global variables. Notice that the globals table here is a permanent
|
||||
// reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is
|
||||
// You can get and set global variables. Notice that the globals table here is a permanent
|
||||
// reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is
|
||||
// based heavily around sharing and internal mutation (just like Lua itself).
|
||||
|
||||
let globals = lua.globals();
|
||||
@@ -20,8 +20,8 @@ fn main() -> Result<()> {
|
||||
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
|
||||
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
|
||||
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// the name of the laoded chunk to "example code", which will be used when Lua error
|
||||
// messages are printed.
|
||||
|
||||
@@ -38,6 +38,17 @@ fn main() -> Result<()> {
|
||||
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
|
||||
assert_eq!(lua.load("return 1 + 2").eval::<i32>()?, 3);
|
||||
|
||||
// Use can use special `chunk!` macro to use Rust tokenizer and automatically capture variables
|
||||
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
let name = "world";
|
||||
lua.load(chunk! {
|
||||
print($a + $b)
|
||||
print("hello, " .. $name)
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
// You can create and manage Lua tables
|
||||
|
||||
let array_table = lua.create_table()?;
|
||||
@@ -76,7 +87,7 @@ fn main() -> Result<()> {
|
||||
let print: Function = globals.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// multiple parameters:
|
||||
|
||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||
@@ -87,15 +98,15 @@ fn main() -> Result<()> {
|
||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||
))?;
|
||||
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// of the arguments can be anything that is convertible from the parameters given by Lua, in
|
||||
// this case, the function expects two string sequences.
|
||||
|
||||
let check_equal = lua.create_function(|_, (list1, list2): (Vec<String>, Vec<String>)| {
|
||||
// This function just checks whether two string lists are equal, and in an inefficient way.
|
||||
// Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return
|
||||
// turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned.
|
||||
// turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned.
|
||||
Ok(list1 == list2)
|
||||
})?;
|
||||
globals.set("check_equal", check_equal)?;
|
||||
@@ -174,7 +185,7 @@ fn main() -> Result<()> {
|
||||
|
||||
lua.scope(|scope| {
|
||||
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
|
||||
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
|
||||
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
|
||||
// because it could be unsafe.
|
||||
|
||||
lua.globals().set(
|
||||
@@ -191,9 +202,9 @@ fn main() -> Result<()> {
|
||||
assert_eq!(rust_val, 42);
|
||||
}
|
||||
|
||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||
// try to run our 'sketchy' function outside of the scope, the function we created will have
|
||||
// been invalidated and we will generate an error. If our function wasn't invalidated, we
|
||||
// been invalidated and we will generate an error. If our function wasn't invalidated, we
|
||||
// might be able to improperly access the freed `rust_val` which would be unsafe.
|
||||
assert!(lua.load("sketchy()").exec().is_err());
|
||||
|
||||
|
||||
@@ -8,10 +8,32 @@ 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())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
}
|
||||
|
||||
impl UserData for Rectangle {
|
||||
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("length", |_, this| Ok(this.length));
|
||||
fields.add_field_method_set("length", |_, this, val| {
|
||||
this.length = val;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("width", |_, this| Ok(this.width));
|
||||
fields.add_field_method_set("width", |_, this, val| {
|
||||
this.width = val;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
|
||||
methods.add_method("diagonal", |_, this, ()| {
|
||||
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
|
||||
});
|
||||
|
||||
// Constructor
|
||||
methods.add_meta_function(MetaMethod::Call, |_, ()| Ok(Rectangle::default()));
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let rectangle = Rectangle::default();
|
||||
lua.load(chunk! {
|
||||
local rect = $rectangle()
|
||||
rect.width = 10
|
||||
rect.length = 5
|
||||
assert(rect:area() == 50)
|
||||
assert(rect:diagonal() - 11.1803 < 0.0001)
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -11,7 +11,14 @@ license = "MIT"
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.5", optional = true }
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
|
||||
use crate::token::{Pos, Token, Tokens};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Capture {
|
||||
key: Token,
|
||||
rust: TokenTree,
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
fn new(key: Token, rust: TokenTree) -> Self {
|
||||
Self { key, rust }
|
||||
}
|
||||
|
||||
/// Token string inside `chunk!`
|
||||
pub(crate) fn key(&self) -> &Token {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// As rust variable, e.g. `x`
|
||||
pub(crate) fn as_rust(&self) -> &TokenTree {
|
||||
&self.rust
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Captures(Vec<Capture>);
|
||||
|
||||
impl Captures {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub(crate) fn add(&mut self, token: &Token) -> Capture {
|
||||
let tt = token.tree();
|
||||
let key = token.clone();
|
||||
|
||||
match self.0.iter().find(|arg| arg.key() == &key) {
|
||||
Some(arg) => arg.clone(),
|
||||
None => {
|
||||
let arg = Capture::new(key, tt.clone());
|
||||
self.0.push(arg.clone());
|
||||
arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Chunk {
|
||||
source: String,
|
||||
caps: Captures,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub(crate) fn new(tokens: TokenStream) -> Self {
|
||||
let tokens = Tokens::retokenize(tokens);
|
||||
|
||||
let mut source = String::new();
|
||||
let mut caps = Captures::new();
|
||||
|
||||
let mut pos: Option<Pos> = None;
|
||||
for t in tokens {
|
||||
if t.is_cap() {
|
||||
caps.add(&t);
|
||||
}
|
||||
|
||||
let (line, col) = (t.start().line, t.start().column);
|
||||
let (prev_line, prev_col) = pos
|
||||
.take()
|
||||
.map(|lc| (lc.line, lc.column))
|
||||
.unwrap_or_else(|| (line, col));
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
if line > prev_line {
|
||||
source.push('\n');
|
||||
} else if line == prev_line {
|
||||
for _ in 0..col.saturating_sub(prev_col) {
|
||||
source.push(' ');
|
||||
}
|
||||
}
|
||||
source.push_str(&t.to_string());
|
||||
|
||||
pos = Some(t.end());
|
||||
}
|
||||
|
||||
Self {
|
||||
source: source.trim_end().to_string(),
|
||||
caps,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
self.caps.captures()
|
||||
}
|
||||
}
|
||||
+100
-18
@@ -1,37 +1,119 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote_spanned;
|
||||
use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as ItemFn);
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
|
||||
if args.len() > 0 {
|
||||
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the macro does not support arguments")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
}
|
||||
|
||||
let span = item.span();
|
||||
let item_name = item.sig.ident.clone();
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site());
|
||||
let func_name = func.sig.ident.clone();
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
|
||||
|
||||
let wrapped = quote_spanned! { span =>
|
||||
mlua::require_module_feature!();
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
#item
|
||||
|
||||
mlua::Lua::init_from_ptr(state)
|
||||
.entrypoint1(#item_name)
|
||||
.unwrap()
|
||||
unsafe extern "C" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
::mlua::Lua::init_from_ptr(state)
|
||||
.entrypoint1(#func_name)
|
||||
.expect("cannot initialize module")
|
||||
}
|
||||
};
|
||||
|
||||
wrapped.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
fn to_ident(tt: &TokenTree) -> TokenStream2 {
|
||||
let s: TokenStream = tt.clone().into();
|
||||
s.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
let chunk = Chunk::new(input);
|
||||
|
||||
let source = chunk.source();
|
||||
|
||||
let caps_len = chunk.captures().len();
|
||||
let caps = chunk.captures().iter().map(|cap| {
|
||||
let cap_name = cap.as_rust().to_string();
|
||||
let cap = to_ident(cap.as_rust());
|
||||
quote! { env.raw_set(#cap_name, #cap)?; }
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use ::std::marker::PhantomData;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
|
||||
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||
{
|
||||
fn source(&self) -> &[u8] {
|
||||
(#source).as_bytes()
|
||||
}
|
||||
|
||||
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", globals.clone())?;
|
||||
meta.raw_set("__newindex", globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
});
|
||||
|
||||
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod chunk;
|
||||
#[cfg(feature = "macros")]
|
||||
mod token;
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
|
||||
use proc_macro2::Span as Span2;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct Pos {
|
||||
pub(crate) line: usize,
|
||||
pub(crate) column: usize,
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
fn new(line: usize, column: usize) -> Self {
|
||||
Self { line, column }
|
||||
}
|
||||
|
||||
fn left(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_sub(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn right(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_add(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let span2: Span2 = (*span).into();
|
||||
let start = span2.start();
|
||||
let end = span2.end();
|
||||
|
||||
// In stable, line/column information is not provided
|
||||
// and set to 0 (line is 1-indexed)
|
||||
if start.line == 0 || end.line == 0 {
|
||||
return fallback_span_pos(span);
|
||||
}
|
||||
|
||||
(
|
||||
Pos::new(start.line, start.column),
|
||||
Pos::new(end.line, end.column),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
// Workaround to somehow retrieve location information in span in stable rust :(
|
||||
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
|
||||
|
||||
match RE.captures(&format!("{:?}", span)) {
|
||||
Some(caps) => match (caps.get(1), caps.get(2)) {
|
||||
(Some(start), Some(end)) => Some((
|
||||
match start.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
match end.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!(
|
||||
"Cannot retrieve span information; please use nightly"
|
||||
),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
/// Attribute of token.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum TokenAttr {
|
||||
/// No attribute
|
||||
None,
|
||||
/// Starts with `$`
|
||||
Cap,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Token {
|
||||
source: String,
|
||||
tree: TokenTree,
|
||||
start: Pos,
|
||||
end: Pos,
|
||||
attr: TokenAttr,
|
||||
}
|
||||
|
||||
impl PartialEq for Token {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.source == other.source && self.attr == other.attr
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Token {}
|
||||
|
||||
impl Token {
|
||||
fn new(tree: TokenTree) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
Self {
|
||||
source: tree.to_string(),
|
||||
start,
|
||||
end,
|
||||
tree,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_delim(source: String, tree: TokenTree, open: bool) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
let (start, end) = if open {
|
||||
(start, start.right())
|
||||
} else {
|
||||
(end.left(), end)
|
||||
};
|
||||
|
||||
Self {
|
||||
source,
|
||||
tree,
|
||||
start,
|
||||
end,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tree(&self) -> &TokenTree {
|
||||
&self.tree
|
||||
}
|
||||
|
||||
pub(crate) fn is_cap(&self) -> bool {
|
||||
self.attr == TokenAttr::Cap
|
||||
}
|
||||
|
||||
pub(crate) fn start(&self) -> Pos {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub(crate) fn end(&self) -> Pos {
|
||||
self.end
|
||||
}
|
||||
|
||||
fn is(&self, s: &str) -> bool {
|
||||
self.source == s
|
||||
}
|
||||
|
||||
fn attr(mut self, attr: TokenAttr) -> Self {
|
||||
self.attr = attr;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Tokens(pub(crate) Vec<Token>);
|
||||
|
||||
impl Tokens {
|
||||
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
|
||||
Tokens(
|
||||
tt.into_iter()
|
||||
.map(Tokens::from)
|
||||
.flatten()
|
||||
.peekable()
|
||||
.batching(|iter| {
|
||||
// Find variable tokens
|
||||
let t = iter.next()?;
|
||||
if t.is("$") {
|
||||
// `$` + `ident` => `$ident`
|
||||
let t = iter.next().expect("$ must trail an identifier");
|
||||
Some(t.attr(TokenAttr::Cap))
|
||||
} else {
|
||||
Some(t)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Tokens {
|
||||
type Item = Token;
|
||||
type IntoIter = IntoIter<Token>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TokenTree> for Tokens {
|
||||
fn from(tt: TokenTree) -> Self {
|
||||
let tts = match tt.clone() {
|
||||
TokenTree::Group(g) => {
|
||||
let (b, e) = match g.delimiter() {
|
||||
Delimiter::Parenthesis => ("(", ")"),
|
||||
Delimiter::Brace => ("{", "}"),
|
||||
Delimiter::Bracket => ("[", "]"),
|
||||
Delimiter::None => ("", ""),
|
||||
};
|
||||
let (b, e) = (b.into(), e.into());
|
||||
|
||||
vec![Token::new_delim(b, tt.clone(), true)]
|
||||
.into_iter()
|
||||
.chain(g.stream().into_iter().map(Tokens::from).flatten())
|
||||
.chain(vec![Token::new_delim(e, tt, false)])
|
||||
.collect()
|
||||
}
|
||||
_ => vec![Token::new(tt)],
|
||||
};
|
||||
Tokens(tts)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.source)
|
||||
}
|
||||
}
|
||||
+39
-12
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
@@ -18,24 +20,28 @@ use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||
|
||||
impl<'lua> ToLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
Ok(lua_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_string(value)?
|
||||
@@ -48,12 +54,14 @@ impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
|
||||
match value {
|
||||
Value::Table(table) => Ok(table),
|
||||
@@ -67,12 +75,14 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
|
||||
match value {
|
||||
Value::Function(table) => Ok(table),
|
||||
@@ -86,12 +96,14 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
|
||||
match value {
|
||||
Value::Thread(t) => Ok(t),
|
||||
@@ -105,12 +117,14 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
@@ -124,12 +138,14 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
|
||||
@@ -143,12 +159,14 @@ impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Error(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
|
||||
match value {
|
||||
Value::Error(err) => Ok(err),
|
||||
@@ -162,12 +180,14 @@ impl<'lua> FromLua<'lua> for Error {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Boolean(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match v {
|
||||
Value::Nil => Ok(false),
|
||||
@@ -178,12 +198,14 @@ impl<'lua> FromLua<'lua> for bool {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for LightUserData {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::LightUserData(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for LightUserData {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
@@ -197,12 +219,14 @@ impl<'lua> FromLua<'lua> for LightUserData {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
@@ -218,6 +242,7 @@ impl<'lua> FromLua<'lua> for StdString {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &str {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
@@ -552,17 +577,17 @@ 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> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
.collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "HashSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -577,22 +602,23 @@ impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
|
||||
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
.collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "BTreeSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
match self {
|
||||
Some(val) => val.to_lua(lua),
|
||||
@@ -602,6 +628,7 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Nil => Ok(None),
|
||||
|
||||
+6
-20
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -88,7 +90,7 @@ pub enum Error {
|
||||
},
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occured inside
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
@@ -152,7 +154,7 @@ pub enum Error {
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust panic that was previosly resumed, returned again.
|
||||
/// A Rust panic that was previously resumed, returned again.
|
||||
///
|
||||
/// This error can occur only when a Rust panic resumed previously was recovered
|
||||
/// and returned again.
|
||||
@@ -172,15 +174,13 @@ pub enum Error {
|
||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
#[cfg(feature = "send")]
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
#[cfg(not(feature = "send"))]
|
||||
ExternalError(Arc<dyn StdError>),
|
||||
}
|
||||
|
||||
/// A specialized `Result` type used by `mlua`'s API.
|
||||
pub type Result<T> = StdResult<T, Error>;
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
@@ -246,7 +246,7 @@ impl fmt::Display for Error {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref traceback, .. } => {
|
||||
write!(fmt, "callback error: {}", traceback)
|
||||
write!(fmt, "callback error\n{}", traceback)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
@@ -275,35 +275,21 @@ impl StdError for Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[cfg(feature = "send")]
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub fn external<T: Into<Box<dyn StdError>>>(err: T) -> Error {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalError {
|
||||
fn to_lua_err(self) -> Error;
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||
fn to_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<E: Into<Box<dyn StdError>>> ExternalError for E {
|
||||
fn to_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalResult<T> {
|
||||
fn to_lua_err(self) -> Result<T>;
|
||||
}
|
||||
|
||||
+2
-1
@@ -538,6 +538,7 @@ pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[allow(clippy::branches_sharing_code)]
|
||||
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||
use super::glue::LUA_EXTRASPACE;
|
||||
|
||||
@@ -679,7 +680,7 @@ pub unsafe fn luaL_traceback(
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
let mut ar: lua_Debug = std::mem::zeroed();
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
||||
|
||||
+24
-3
@@ -80,6 +80,7 @@ pub const LUA_ERRERR: c_int = 5;
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
pub const LUA_ERRERR: c_int = 6;
|
||||
|
||||
/// A raw Lua state associated with a thread.
|
||||
pub type lua_State = c_void;
|
||||
|
||||
// basic types
|
||||
@@ -111,20 +112,24 @@ pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
|
||||
|
||||
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses `luaL_Buffer`
|
||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
/// A Lua number, usually equivalent to `f64`.
|
||||
pub type lua_Number = luaconf::LUA_NUMBER;
|
||||
|
||||
/// A Lua integer, usually equivalent to `i64`.
|
||||
pub type lua_Integer = luaconf::LUA_INTEGER;
|
||||
|
||||
// unsigned integer type
|
||||
/// A Lua unsigned integer, usually equivalent to `u64`.
|
||||
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
|
||||
|
||||
// type for continuation-function contexts
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
pub type lua_KContext = luaconf::LUA_KCONTEXT;
|
||||
|
||||
/// Type for native functions that can be passed to Lua.
|
||||
/// Type for native C functions that can be passed to Lua.
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Type for continuation functions
|
||||
@@ -156,8 +161,13 @@ extern "C" {
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_54(L: *mut lua_State) -> c_int;
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_jit(L: *mut lua_State, th: *mut lua_State);
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
@@ -216,6 +226,17 @@ extern "C" {
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub unsafe fn lua_resetthread(_L: *mut lua_State, th: *mut lua_State) -> c_int {
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
{
|
||||
lua_resetthread_jit(_L, th);
|
||||
LUA_OK
|
||||
}
|
||||
#[cfg(feature = "lua54")]
|
||||
lua_resetthread_54(th)
|
||||
}
|
||||
|
||||
// Comparison and arithmetic functions
|
||||
pub const LUA_OPADD: c_int = 0;
|
||||
pub const LUA_OPSUB: c_int = 1;
|
||||
|
||||
+30
-7
@@ -160,8 +160,8 @@ pub use self::lua::{
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub use self::lua::{
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_resetthread, lua_setcstacklimit, lua_setiuservalue,
|
||||
lua_setwarnf, lua_toclose, lua_warning,
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_setcstacklimit, lua_setiuservalue, lua_setwarnf,
|
||||
lua_toclose, lua_warning,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
@@ -170,6 +170,9 @@ pub use self::lua::{lua_isyieldable, lua_version};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub use self::lua::lua_resetthread;
|
||||
|
||||
// auxiliary library types
|
||||
pub use self::lauxlib::luaL_Reg;
|
||||
|
||||
@@ -212,7 +215,8 @@ pub use self::lua::{
|
||||
LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, LUA_OPADD, LUA_OPDIV, LUA_OPEQ,
|
||||
LUA_OPLE, LUA_OPLT, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSUB, LUA_OPUNM, LUA_REGISTRYINDEX,
|
||||
LUA_SIGNATURE, LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE,
|
||||
LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
|
||||
LUA_TNUMBER, LUA_TRACEBACK_STACK, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
|
||||
LUA_YIELD,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
@@ -253,16 +257,18 @@ pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
|
||||
// Not actually defined in lua.h / luaconf.h
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/alloc.rs
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon"
|
||||
target_arch = "hexagon",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
@@ -275,6 +281,25 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
)))]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
|
||||
// Hack to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
pub(crate) fn keep_lua_symbols() {
|
||||
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
|
||||
symbols.push(lua_atpanic as _);
|
||||
symbols.push(lua_isuserdata as _);
|
||||
symbols.push(lua_tocfunction as _);
|
||||
symbols.push(luaL_loadstring as _);
|
||||
symbols.push(luaL_openlibs as _);
|
||||
if cfg!(any(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52"
|
||||
))) {
|
||||
symbols.push(lua_getglobal as _);
|
||||
symbols.push(lua_setglobal as _);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_imports, dead_code, non_camel_case_types)]
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
mod glue {
|
||||
@@ -288,5 +313,3 @@ mod lauxlib;
|
||||
mod lua;
|
||||
mod luaconf;
|
||||
mod lualib;
|
||||
|
||||
pub mod safe;
|
||||
|
||||
-278
@@ -1,278 +0,0 @@
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::protect_lua;
|
||||
|
||||
use super::lua::{lua_CFunction, lua_Debug, lua_Integer, lua_State};
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "MLUA_WRAPPED_ERROR_SIZE"]
|
||||
pub static mut WRAPPED_ERROR_SIZE: usize;
|
||||
#[link_name = "MLUA_WRAPPED_PANIC_SIZE"]
|
||||
pub static mut WRAPPED_PANIC_SIZE: usize;
|
||||
#[link_name = "MLUA_WRAPPED_ERROR_KEY"]
|
||||
pub static mut WRAPPED_ERROR_KEY: *const c_void;
|
||||
#[link_name = "MLUA_WRAPPED_PANIC_KEY"]
|
||||
pub static mut WRAPPED_PANIC_KEY: *const c_void;
|
||||
|
||||
pub fn lua_call_mlua_hook_proc(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
pub fn meta_index_impl(state: *mut lua_State) -> c_int;
|
||||
pub fn meta_newindex_impl(state: *mut lua_State) -> c_int;
|
||||
pub fn bind_call_impl(state: *mut lua_State) -> c_int;
|
||||
pub fn error_traceback(state: *mut lua_State) -> c_int;
|
||||
pub fn lua_nopanic_pcall(state: *mut lua_State) -> c_int;
|
||||
pub fn lua_nopanic_xpcall(state: *mut lua_State) -> c_int;
|
||||
|
||||
fn lua_gc_s(L: *mut lua_State) -> c_int;
|
||||
fn luaL_ref_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_pushlstring_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_tolstring_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_newthread_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_newuserdata_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_newwrappederror_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_pushcclosure_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_pushrclosure_s(L: *mut lua_State) -> c_int;
|
||||
fn luaL_requiref_s(L: *mut lua_State) -> c_int;
|
||||
fn error_traceback_s(L: *mut lua_State) -> c_int;
|
||||
|
||||
fn lua_newtable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_createtable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_gettable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_settable_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_geti_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawset_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawseti_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawsetp_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawsetfield_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawinsert_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_rawremove_s(L: *mut lua_State) -> c_int;
|
||||
fn luaL_len_s(L: *mut lua_State) -> c_int;
|
||||
fn lua_next_s(L: *mut lua_State) -> c_int;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct StringArg {
|
||||
data: *const c_char,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
//
|
||||
// Common functions
|
||||
//
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_gc(state: *mut lua_State, what: c_int, data: c_int) -> Result<c_int> {
|
||||
super::lua_pushinteger(state, what as lua_Integer);
|
||||
super::lua_pushinteger(state, data as lua_Integer);
|
||||
protect_lua(state, 2, lua_gc_s)?;
|
||||
let ret = super::lua_tointeger(state, -1) as c_int;
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn luaL_ref(state: *mut lua_State, table: c_int) -> Result<c_int> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
protect_lua(state, 2, luaL_ref_s)?;
|
||||
let ret = super::lua_tointeger(state, -1) as c_int;
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_pushstring<S: AsRef<[u8]> + ?Sized>(state: *mut lua_State, s: &S) -> Result<()> {
|
||||
let s = s.as_ref();
|
||||
let s = StringArg {
|
||||
data: s.as_ptr() as *const c_char,
|
||||
len: s.len(),
|
||||
};
|
||||
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
|
||||
protect_lua(state, 1, lua_pushlstring_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_tolstring(
|
||||
state: *mut lua_State,
|
||||
index: c_int,
|
||||
len: *mut usize,
|
||||
) -> Result<*const c_char> {
|
||||
let index = super::lua_absindex(state, index);
|
||||
super::lua_pushvalue(state, index);
|
||||
super::lua_pushlightuserdata(state, len as *mut c_void);
|
||||
protect_lua(state, 2, lua_tolstring_s)?;
|
||||
let s = super::lua_touserdata(state, -1);
|
||||
super::lua_pop(state, 1);
|
||||
super::lua_replace(state, index);
|
||||
Ok(s as *const c_char)
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces
|
||||
pub unsafe fn lua_newthread(state: *mut lua_State) -> Result<*mut lua_State> {
|
||||
protect_lua(state, 0, lua_newthread_s)?;
|
||||
Ok(super::lua_tothread(state, -1))
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut c_void> {
|
||||
super::lua_pushinteger(state, size as lua_Integer);
|
||||
protect_lua(state, 1, lua_newuserdata_s)?;
|
||||
Ok(super::lua_touserdata(state, -1))
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces
|
||||
pub unsafe fn lua_newwrappederror(state: *mut lua_State) -> Result<*mut c_void> {
|
||||
protect_lua(state, 0, lua_newwrappederror_s)?;
|
||||
Ok(super::lua_touserdata(state, -1))
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
|
||||
super::lua_pushlightuserdata(state, f as *mut c_void);
|
||||
protect_lua(state, n + 1, lua_pushcclosure_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_pushrclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
|
||||
super::lua_pushlightuserdata(state, f as *mut c_void);
|
||||
if n > 0 {
|
||||
super::lua_rotate(state, -n - 1, 1);
|
||||
}
|
||||
protect_lua(state, n + 1, lua_pushrclosure_s)
|
||||
}
|
||||
|
||||
// Uses 5 stack spaces
|
||||
pub unsafe fn luaL_requiref<S: AsRef<[u8]> + ?Sized>(
|
||||
state: *mut lua_State,
|
||||
modname: &S,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) -> Result<()> {
|
||||
let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes");
|
||||
super::lua_pushlightuserdata(state, modname.as_ptr() as *mut c_void);
|
||||
super::lua_pushlightuserdata(state, openf as *mut c_void);
|
||||
super::lua_pushinteger(state, glb as lua_Integer);
|
||||
protect_lua(state, 3, luaL_requiref_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) -> Result<()> {
|
||||
mlua_assert!(
|
||||
state != state2,
|
||||
"error_traceback2 must be used with two different states"
|
||||
);
|
||||
super::lua_pushlightuserdata(state, state2);
|
||||
protect_lua(state, 1, error_traceback_s)
|
||||
}
|
||||
|
||||
//
|
||||
// Table functions
|
||||
//
|
||||
|
||||
// Uses 2 stack spaces
|
||||
pub unsafe fn lua_newtable(state: *mut lua_State) -> Result<()> {
|
||||
protect_lua(state, 0, lua_newtable_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> {
|
||||
super::lua_pushinteger(state, narr as lua_Integer);
|
||||
super::lua_pushinteger(state, nrec as lua_Integer);
|
||||
protect_lua(state, 2, lua_createtable_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_gettable(state: *mut lua_State, table: c_int) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
protect_lua(state, 2, lua_gettable_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_settable(state: *mut lua_State, table: c_int) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -3, 1);
|
||||
protect_lua(state, 3, lua_settable_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_geti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<c_int> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 2, lua_geti_s).map(|_| super::lua_type(state, -1))
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_rawset(state: *mut lua_State, table: c_int) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -3, 1);
|
||||
protect_lua(state, 3, lua_rawset_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawseti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 3, lua_rawseti_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawsetp(state: *mut lua_State, table: c_int, ptr: *const c_void) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
super::lua_pushlightuserdata(state, ptr as *mut c_void);
|
||||
protect_lua(state, 3, lua_rawsetp_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawsetfield<S>(state: *mut lua_State, table: c_int, field: &S) -> Result<()>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
let field = field.as_ref();
|
||||
let s = StringArg {
|
||||
data: field.as_ptr() as *const c_char,
|
||||
len: field.len(),
|
||||
};
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
|
||||
super::lua_rotate(state, -3, 2);
|
||||
protect_lua(state, 3, lua_rawsetfield_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawinsert(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 3, lua_rawinsert_s)
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces
|
||||
pub unsafe fn lua_rawremove(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_pushinteger(state, i);
|
||||
protect_lua(state, 2, lua_rawremove_s)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn luaL_len(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
|
||||
super::lua_pushvalue(state, table);
|
||||
protect_lua(state, 1, luaL_len_s)?;
|
||||
let ret = super::lua_tointeger(state, -1);
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
pub unsafe fn lua_next(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
|
||||
super::lua_pushvalue(state, table);
|
||||
super::lua_rotate(state, -2, 1);
|
||||
protect_lua(state, 2, lua_next_s)?;
|
||||
let ret = super::lua_tointeger(state, -1);
|
||||
super::lua_pop(state, 1);
|
||||
Ok(ret)
|
||||
}
|
||||
@@ -1,953 +0,0 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "compat-5.3.h"
|
||||
|
||||
/* don't compile it again if it already is included via compat53.h */
|
||||
#ifndef COMPAT53_C_
|
||||
#define COMPAT53_C_
|
||||
|
||||
|
||||
|
||||
/* definitions for Lua 5.1 only */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||
|
||||
#ifndef COMPAT53_FOPEN_NO_LOCK
|
||||
# if defined(_MSC_VER)
|
||||
# define COMPAT53_FOPEN_NO_LOCK 1
|
||||
# else /* otherwise */
|
||||
# define COMPAT53_FOPEN_NO_LOCK 0
|
||||
# endif /* VC++ only so far */
|
||||
#endif /* No-lock fopen_s usage if possible */
|
||||
|
||||
#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK
|
||||
# include <share.h>
|
||||
#endif /* VC++ _fsopen for share-allowed file read */
|
||||
|
||||
#ifndef COMPAT53_HAVE_STRERROR_R
|
||||
# if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
|
||||
(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \
|
||||
defined(__APPLE__)
|
||||
# define COMPAT53_HAVE_STRERROR_R 1
|
||||
# else /* none of the defines matched: define to 0 */
|
||||
# define COMPAT53_HAVE_STRERROR_R 0
|
||||
# endif /* have strerror_r of some form */
|
||||
#endif /* strerror_r */
|
||||
|
||||
#ifndef COMPAT53_HAVE_STRERROR_S
|
||||
# if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \
|
||||
defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__)
|
||||
# define COMPAT53_HAVE_STRERROR_S 1
|
||||
# else /* not VC++ or C11 */
|
||||
# define COMPAT53_HAVE_STRERROR_S 0
|
||||
# endif /* strerror_s from VC++ or C11 */
|
||||
#endif /* strerror_s */
|
||||
|
||||
#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE
|
||||
# define COMPAT53_LUA_FILE_BUFFER_SIZE 4096
|
||||
#endif /* Lua File Buffer Size */
|
||||
|
||||
|
||||
static char* compat53_strerror (int en, char* buff, size_t sz) {
|
||||
#if COMPAT53_HAVE_STRERROR_R
|
||||
/* use strerror_r here, because it's available on these specific platforms */
|
||||
if (sz > 0) {
|
||||
buff[0] = '\0';
|
||||
/* we don't care whether the GNU version or the XSI version is used: */
|
||||
if (strerror_r(en, buff, sz)) {
|
||||
/* Yes, we really DO want to ignore the return value!
|
||||
* GCC makes that extra hard, not even a (void) cast will do. */
|
||||
}
|
||||
if (buff[0] == '\0') {
|
||||
/* Buffer is unchanged, so we probably have called GNU strerror_r which
|
||||
* returned a static constant string. Chances are that strerror will
|
||||
* return the same static constant string and therefore be thread-safe. */
|
||||
return strerror(en);
|
||||
}
|
||||
}
|
||||
return buff; /* sz is 0 *or* strerror_r wrote into the buffer */
|
||||
#elif COMPAT53_HAVE_STRERROR_S
|
||||
/* for MSVC and other C11 implementations, use strerror_s since it's
|
||||
* provided by default by the libraries */
|
||||
strerror_s(buff, sz, en);
|
||||
return buff;
|
||||
#else
|
||||
/* fallback, but strerror is not guaranteed to be threadsafe due to modifying
|
||||
* errno itself and some impls not locking a static buffer for it ... but most
|
||||
* known systems have threadsafe errno: this might only change if the locale
|
||||
* is changed out from under someone while this function is being called */
|
||||
(void)buff;
|
||||
(void)sz;
|
||||
return strerror(en);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_absindex (lua_State *L, int i) {
|
||||
if (i < 0 && i > LUA_REGISTRYINDEX)
|
||||
i += lua_gettop(L) + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static void compat53_call_lua (lua_State *L, char const code[], size_t len,
|
||||
int nargs, int nret) {
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);
|
||||
if (lua_type(L, -1) != LUA_TFUNCTION) {
|
||||
lua_pop(L, 1);
|
||||
if (luaL_loadbuffer(L, code, len, "=none"))
|
||||
lua_error(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);
|
||||
}
|
||||
lua_insert(L, -nargs-1);
|
||||
lua_call(L, nargs, nret);
|
||||
}
|
||||
|
||||
|
||||
static const char compat53_arith_code[] =
|
||||
"local op,a,b=...\n"
|
||||
"if op==0 then return a+b\n"
|
||||
"elseif op==1 then return a-b\n"
|
||||
"elseif op==2 then return a*b\n"
|
||||
"elseif op==3 then return a/b\n"
|
||||
"elseif op==4 then return a%b\n"
|
||||
"elseif op==5 then return a^b\n"
|
||||
"elseif op==6 then return -a\n"
|
||||
"end\n";
|
||||
|
||||
COMPAT53_API void lua_arith (lua_State *L, int op) {
|
||||
if (op < LUA_OPADD || op > LUA_OPUNM)
|
||||
luaL_error(L, "invalid 'op' argument for lua_arith");
|
||||
luaL_checkstack(L, 5, "not enough stack slots");
|
||||
if (op == LUA_OPUNM)
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushnumber(L, op);
|
||||
lua_insert(L, -3);
|
||||
compat53_call_lua(L, compat53_arith_code,
|
||||
sizeof(compat53_arith_code)-1, 3, 1);
|
||||
}
|
||||
|
||||
|
||||
static const char compat53_compare_code[] =
|
||||
"local a,b=...\n"
|
||||
"return a<=b\n";
|
||||
|
||||
COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) {
|
||||
int result = 0;
|
||||
switch (op) {
|
||||
case LUA_OPEQ:
|
||||
return lua_equal(L, idx1, idx2);
|
||||
case LUA_OPLT:
|
||||
return lua_lessthan(L, idx1, idx2);
|
||||
case LUA_OPLE:
|
||||
luaL_checkstack(L, 5, "not enough stack slots");
|
||||
idx1 = lua_absindex(L, idx1);
|
||||
idx2 = lua_absindex(L, idx2);
|
||||
lua_pushvalue(L, idx1);
|
||||
lua_pushvalue(L, idx2);
|
||||
compat53_call_lua(L, compat53_compare_code,
|
||||
sizeof(compat53_compare_code)-1, 2, 1);
|
||||
result = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
default:
|
||||
luaL_error(L, "invalid 'op' argument for lua_compare");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_copy (lua_State *L, int from, int to) {
|
||||
int abs_to = lua_absindex(L, to);
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
lua_pushvalue(L, from);
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_len (lua_State *L, int i) {
|
||||
switch (lua_type(L, i)) {
|
||||
case LUA_TSTRING:
|
||||
lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
|
||||
break;
|
||||
case LUA_TTABLE:
|
||||
if (!luaL_callmeta(L, i, "__len"))
|
||||
lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
|
||||
break;
|
||||
case LUA_TUSERDATA:
|
||||
if (luaL_callmeta(L, i, "__len"))
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
luaL_error(L, "attempt to get length of a %s value",
|
||||
lua_typename(L, lua_type(L, i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) {
|
||||
int abs_i = lua_absindex(L, i);
|
||||
lua_pushlightuserdata(L, (void*)p);
|
||||
lua_rawget(L, abs_i);
|
||||
return lua_type(L, -1);
|
||||
}
|
||||
|
||||
COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) {
|
||||
int abs_i = lua_absindex(L, i);
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
lua_pushlightuserdata(L, (void*)p);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) {
|
||||
lua_Number n = lua_tonumber(L, i);
|
||||
if (isnum != NULL) {
|
||||
*isnum = (n != 0 || lua_isnumber(L, i));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_checkversion (lua_State *L) {
|
||||
(void)L;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) {
|
||||
if (!lua_checkstack(L, sp+LUA_MINSTACK)) {
|
||||
if (msg != NULL)
|
||||
luaL_error(L, "stack overflow (%s)", msg);
|
||||
else {
|
||||
lua_pushliteral(L, "stack overflow");
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) {
|
||||
int abs_i = lua_absindex(L, i);
|
||||
luaL_checkstack(L, 3, "not enough stack slots");
|
||||
lua_pushstring(L, name);
|
||||
lua_gettable(L, abs_i);
|
||||
if (lua_istable(L, -1))
|
||||
return 1;
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, abs_i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) {
|
||||
lua_Integer res = 0;
|
||||
int isnum = 0;
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
lua_len(L, i);
|
||||
res = lua_tointegerx(L, -1, &isnum);
|
||||
lua_pop(L, 1);
|
||||
if (!isnum)
|
||||
luaL_error(L, "object length is not an integer");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||
luaL_checkstack(L, nup+1, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
lua_pushstring(L, l->name);
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -(nup + 1));
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */
|
||||
}
|
||||
lua_pop(L, nup); /* remove upvalues */
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) {
|
||||
luaL_checkstack(L, 1, "not enough stack slots");
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) {
|
||||
void *p = lua_touserdata(L, i);
|
||||
luaL_checkstack(L, 2, "not enough stack slots");
|
||||
if (p == NULL || !lua_getmetatable(L, i))
|
||||
return NULL;
|
||||
else {
|
||||
int res = 0;
|
||||
luaL_getmetatable(L, tname);
|
||||
res = lua_rawequal(L, -1, -2);
|
||||
lua_pop(L, 2);
|
||||
if (!res)
|
||||
p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static int compat53_countlevels (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int li = 1, le = 1;
|
||||
/* find an upper bound */
|
||||
while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
|
||||
/* do a binary search */
|
||||
while (li < le) {
|
||||
int m = (li + le)/2;
|
||||
if (lua_getstack(L, m, &ar)) li = m + 1;
|
||||
else le = m;
|
||||
}
|
||||
return le - 1;
|
||||
}
|
||||
|
||||
static int compat53_findfield (lua_State *L, int objidx, int level) {
|
||||
if (level == 0 || !lua_istable(L, -1))
|
||||
return 0; /* not found */
|
||||
lua_pushnil(L); /* start 'next' loop */
|
||||
while (lua_next(L, -2)) { /* for each pair in table */
|
||||
if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
|
||||
if (lua_rawequal(L, objidx, -1)) { /* found object? */
|
||||
lua_pop(L, 1); /* remove value (but keep name) */
|
||||
return 1;
|
||||
}
|
||||
else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */
|
||||
lua_remove(L, -2); /* remove table (but keep name) */
|
||||
lua_pushliteral(L, ".");
|
||||
lua_insert(L, -2); /* place '.' between the two names */
|
||||
lua_concat(L, 3);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1); /* remove value */
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) {
|
||||
int top = lua_gettop(L);
|
||||
lua_getinfo(L, "f", ar); /* push function */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
if (compat53_findfield(L, top + 1, 2)) {
|
||||
lua_copy(L, -1, top + 1); /* move name to proper place */
|
||||
lua_pop(L, 2); /* remove pushed values */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_settop(L, top); /* remove function and global table */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) {
|
||||
if (*ar->namewhat != '\0') /* is there a name? */
|
||||
lua_pushfstring(L, "function " LUA_QS, ar->name);
|
||||
else if (*ar->what == 'm') /* main? */
|
||||
lua_pushliteral(L, "main chunk");
|
||||
else if (*ar->what == 'C') {
|
||||
if (compat53_pushglobalfuncname(L, ar)) {
|
||||
lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
|
||||
lua_remove(L, -2); /* remove name */
|
||||
}
|
||||
else
|
||||
lua_pushliteral(L, "?");
|
||||
}
|
||||
else
|
||||
lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
|
||||
}
|
||||
|
||||
#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */
|
||||
#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */
|
||||
|
||||
COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1,
|
||||
const char *msg, int level) {
|
||||
lua_Debug ar;
|
||||
int top = lua_gettop(L);
|
||||
int numlevels = compat53_countlevels(L1);
|
||||
int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0;
|
||||
if (msg) lua_pushfstring(L, "%s\n", msg);
|
||||
lua_pushliteral(L, "stack traceback:");
|
||||
while (lua_getstack(L1, level++, &ar)) {
|
||||
if (level == mark) { /* too many levels? */
|
||||
lua_pushliteral(L, "\n\t..."); /* add a '...' */
|
||||
level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */
|
||||
}
|
||||
else {
|
||||
lua_getinfo(L1, "Slnt", &ar);
|
||||
lua_pushfstring(L, "\n\t%s:", ar.short_src);
|
||||
if (ar.currentline > 0)
|
||||
lua_pushfstring(L, "%d:", ar.currentline);
|
||||
lua_pushliteral(L, " in ");
|
||||
compat53_pushfuncname(L, &ar);
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
}
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
||||
const char *serr = NULL;
|
||||
int en = errno; /* calls to Lua API may change this value */
|
||||
char buf[512] = { 0 };
|
||||
if (stat) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
serr = compat53_strerror(en, buf, sizeof(buf));
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, serr);
|
||||
else
|
||||
lua_pushstring(L, serr);
|
||||
lua_pushnumber(L, (lua_Number)en);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) {
|
||||
if (mode && strchr(mode, modename[0]) == NULL) {
|
||||
lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode);
|
||||
return err;
|
||||
}
|
||||
return LUA_OK;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
lua_Reader reader;
|
||||
void *ud;
|
||||
int has_peeked_data;
|
||||
const char *peeked_data;
|
||||
size_t peeked_data_size;
|
||||
} compat53_reader_data;
|
||||
|
||||
|
||||
static const char *compat53_reader (lua_State *L, void *ud, size_t *size) {
|
||||
compat53_reader_data *data = (compat53_reader_data *)ud;
|
||||
if (data->has_peeked_data) {
|
||||
data->has_peeked_data = 0;
|
||||
*size = data->peeked_data_size;
|
||||
return data->peeked_data;
|
||||
} else
|
||||
return data->reader(L, data->ud, size);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) {
|
||||
int status = LUA_OK;
|
||||
compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 };
|
||||
compat53_data.reader = reader;
|
||||
compat53_data.ud = data;
|
||||
compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size));
|
||||
if (compat53_data.peeked_data && compat53_data.peeked_data_size &&
|
||||
compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */
|
||||
status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
|
||||
else
|
||||
status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
|
||||
if (status != LUA_OK)
|
||||
return status;
|
||||
/* we need to call the original 5.1 version of lua_load! */
|
||||
#undef lua_load
|
||||
return lua_load(L, compat53_reader, &compat53_data, source);
|
||||
#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
int n; /* number of pre-read characters */
|
||||
FILE *f; /* file being read */
|
||||
char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */
|
||||
} compat53_LoadF;
|
||||
|
||||
|
||||
static const char *compat53_getF (lua_State *L, void *ud, size_t *size) {
|
||||
compat53_LoadF *lf = (compat53_LoadF *)ud;
|
||||
(void)L; /* not used */
|
||||
if (lf->n > 0) { /* are there pre-read characters to be read? */
|
||||
*size = lf->n; /* return them (chars already in buffer) */
|
||||
lf->n = 0; /* no more pre-read characters */
|
||||
}
|
||||
else { /* read a block from file */
|
||||
/* 'fread' can return > 0 *and* set the EOF flag. If next call to
|
||||
'compat53_getF' called 'fread', it might still wait for user input.
|
||||
The next check avoids this problem. */
|
||||
if (feof(lf->f)) return NULL;
|
||||
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
|
||||
}
|
||||
return lf->buff;
|
||||
}
|
||||
|
||||
|
||||
static int compat53_errfile (lua_State *L, const char *what, int fnameindex) {
|
||||
char buf[512] = {0};
|
||||
const char *serr = compat53_strerror(errno, buf, sizeof(buf));
|
||||
const char *filename = lua_tostring(L, fnameindex) + 1;
|
||||
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
|
||||
lua_remove(L, fnameindex);
|
||||
return LUA_ERRFILE;
|
||||
}
|
||||
|
||||
|
||||
static int compat53_skipBOM (compat53_LoadF *lf) {
|
||||
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
|
||||
int c;
|
||||
lf->n = 0;
|
||||
do {
|
||||
c = getc(lf->f);
|
||||
if (c == EOF || c != *(const unsigned char *)p++) return c;
|
||||
lf->buff[lf->n++] = (char)c; /* to be read by the parser */
|
||||
} while (*p != '\0');
|
||||
lf->n = 0; /* prefix matched; discard it */
|
||||
return getc(lf->f); /* return next character */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** reads the first character of file 'f' and skips an optional BOM mark
|
||||
** in its beginning plus its first line if it starts with '#'. Returns
|
||||
** true if it skipped the first line. In any case, '*cp' has the
|
||||
** first "valid" character of the file (after the optional BOM and
|
||||
** a first-line comment).
|
||||
*/
|
||||
static int compat53_skipcomment (compat53_LoadF *lf, int *cp) {
|
||||
int c = *cp = compat53_skipBOM(lf);
|
||||
if (c == '#') { /* first line is a comment (Unix exec. file)? */
|
||||
do { /* skip first line */
|
||||
c = getc(lf->f);
|
||||
} while (c != EOF && c != '\n');
|
||||
*cp = getc(lf->f); /* skip end-of-line, if present */
|
||||
return 1; /* there was a comment */
|
||||
}
|
||||
else return 0; /* no comment */
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) {
|
||||
compat53_LoadF lf;
|
||||
int status, readstatus;
|
||||
int c;
|
||||
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
|
||||
if (filename == NULL) {
|
||||
lua_pushliteral(L, "=stdin");
|
||||
lf.f = stdin;
|
||||
}
|
||||
else {
|
||||
lua_pushfstring(L, "@%s", filename);
|
||||
#if defined(_MSC_VER)
|
||||
/* This code is here to stop a deprecation error that stops builds
|
||||
* if a certain macro is defined. While normally not caring would
|
||||
* be best, some header-only libraries and builds can't afford to
|
||||
* dictate this to the user. A quick check shows that fopen_s this
|
||||
* goes back to VS 2005, and _fsopen goes back to VS 2003 .NET,
|
||||
* possibly even before that so we don't need to do any version
|
||||
* number checks, since this has been there since forever. */
|
||||
|
||||
/* TO USER: if you want the behavior of typical fopen_s/fopen,
|
||||
* which does lock the file on VC++, define the macro used below to 0 */
|
||||
#if COMPAT53_FOPEN_NO_LOCK
|
||||
lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */
|
||||
if (lf.f == NULL)
|
||||
return compat53_errfile(L, "open", fnameindex);
|
||||
#else /* use default locking version */
|
||||
if (fopen_s(&lf.f, filename, "r") != 0)
|
||||
return compat53_errfile(L, "open", fnameindex);
|
||||
#endif /* Locking vs. No-locking fopen variants */
|
||||
#else
|
||||
lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */
|
||||
if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex);
|
||||
#endif
|
||||
}
|
||||
if (compat53_skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
#if defined(_MSC_VER)
|
||||
if (freopen_s(&lf.f, filename, "rb", lf.f) != 0)
|
||||
return compat53_errfile(L, "reopen", fnameindex);
|
||||
#else
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex);
|
||||
#endif
|
||||
compat53_skipcomment(&lf, &c); /* re-read initial portion */
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */
|
||||
status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode);
|
||||
readstatus = ferror(lf.f);
|
||||
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
||||
if (readstatus) {
|
||||
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
||||
return compat53_errfile(L, "read", fnameindex);
|
||||
}
|
||||
lua_remove(L, fnameindex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) {
|
||||
int status = LUA_OK;
|
||||
if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) {
|
||||
status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
|
||||
}
|
||||
else {
|
||||
status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
|
||||
}
|
||||
if (status != LUA_OK)
|
||||
return status;
|
||||
return luaL_loadbuffer(L, buff, sz, name);
|
||||
}
|
||||
|
||||
|
||||
#if !defined(l_inspectstat) && \
|
||||
(defined(unix) || defined(__unix) || defined(__unix__) || \
|
||||
defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \
|
||||
(defined(__APPLE__) && defined(__MACH__)))
|
||||
/* some form of unix; check feature macros in unistd.h for details */
|
||||
# include <unistd.h>
|
||||
/* check posix version; the relevant include files and macros probably
|
||||
* were available before 2001, but I'm not sure */
|
||||
# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
|
||||
# include <sys/wait.h>
|
||||
# define l_inspectstat(stat,what) \
|
||||
if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \
|
||||
else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* provide default (no-op) version */
|
||||
#if !defined(l_inspectstat)
|
||||
# define l_inspectstat(stat,what) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
COMPAT53_API int luaL_execresult (lua_State *L, int stat) {
|
||||
const char *what = "exit";
|
||||
if (stat == -1)
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
else {
|
||||
l_inspectstat(stat, what);
|
||||
if (*what == 'e' && stat == 0)
|
||||
lua_pushboolean(L, 1);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, what);
|
||||
lua_pushinteger(L, stat);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) {
|
||||
/* make it crash if used via pointer to a 5.1-style luaL_Buffer */
|
||||
B->b.p = NULL;
|
||||
B->b.L = NULL;
|
||||
B->b.lvl = 0;
|
||||
/* reuse the buffer from the 5.1-style luaL_Buffer though! */
|
||||
B->ptr = B->b.buffer;
|
||||
B->capacity = LUAL_BUFFERSIZE;
|
||||
B->nelems = 0;
|
||||
B->L2 = L;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) {
|
||||
if (B->capacity - B->nelems < s) { /* needs to grow */
|
||||
char* newptr = NULL;
|
||||
size_t newcap = B->capacity * 2;
|
||||
if (newcap - B->nelems < s)
|
||||
newcap = B->nelems + s;
|
||||
if (newcap < B->capacity) /* overflow */
|
||||
luaL_error(B->L2, "buffer too large");
|
||||
newptr = (char*)lua_newuserdata(B->L2, newcap);
|
||||
memcpy(newptr, B->ptr, B->nelems);
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -2); /* remove old buffer */
|
||||
B->ptr = newptr;
|
||||
B->capacity = newcap;
|
||||
}
|
||||
return B->ptr+B->nelems;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) {
|
||||
memcpy(luaL_prepbuffsize(B, l), s, l);
|
||||
luaL_addsize(B, l);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) {
|
||||
size_t len = 0;
|
||||
const char *s = lua_tolstring(B->L2, -1, &len);
|
||||
if (!s)
|
||||
luaL_error(B->L2, "cannot convert value to string");
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_insert(B->L2, -2); /* userdata buffer must be at stack top */
|
||||
luaL_addlstring(B, s, len);
|
||||
lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);
|
||||
}
|
||||
|
||||
|
||||
void luaL_pushresult (luaL_Buffer_53 *B) {
|
||||
lua_pushlstring(B->L2, B->ptr, B->nelems);
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -2); /* remove userdata buffer */
|
||||
}
|
||||
|
||||
|
||||
#endif /* Lua 5.1 */
|
||||
|
||||
|
||||
|
||||
/* definitions for Lua 5.1 and Lua 5.2 */
|
||||
#if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502
|
||||
|
||||
|
||||
COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) {
|
||||
index = lua_absindex(L, index);
|
||||
lua_pushinteger(L, i);
|
||||
lua_gettable(L, index);
|
||||
return lua_type(L, -1);
|
||||
}
|
||||
|
||||
|
||||
#ifndef LUA_EXTRASPACE
|
||||
#define LUA_EXTRASPACE (sizeof(void*))
|
||||
#endif
|
||||
|
||||
COMPAT53_API void *lua_getextraspace (lua_State *L) {
|
||||
int is_main = 0;
|
||||
void *ptr = NULL;
|
||||
luaL_checkstack(L, 4, "not enough stack slots available");
|
||||
lua_pushliteral(L, "__compat53_extraspace");
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_pushliteral(L, "k");
|
||||
lua_setfield(L, -2, "__mode");
|
||||
lua_setmetatable(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
is_main = lua_pushthread(L);
|
||||
lua_rawget(L, -2);
|
||||
ptr = lua_touserdata(L, -1);
|
||||
if (!ptr) {
|
||||
lua_pop(L, 1);
|
||||
ptr = lua_newuserdata(L, LUA_EXTRASPACE);
|
||||
if (is_main) {
|
||||
memset(ptr, '\0', LUA_EXTRASPACE);
|
||||
lua_pushthread(L);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, -4);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, -4);
|
||||
} else {
|
||||
void* mptr = NULL;
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawget(L, -3);
|
||||
mptr = lua_touserdata(L, -1);
|
||||
if (mptr)
|
||||
memcpy(ptr, mptr, LUA_EXTRASPACE);
|
||||
else
|
||||
memset(ptr, '\0', LUA_EXTRASPACE);
|
||||
lua_pop(L, 1);
|
||||
lua_pushthread(L);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, -4);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 2);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API int lua_isinteger (lua_State *L, int index) {
|
||||
if (lua_type(L, index) == LUA_TNUMBER) {
|
||||
lua_Number n = lua_tonumber(L, index);
|
||||
lua_Integer i = lua_tointeger(L, index);
|
||||
if (i == n)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) {
|
||||
int ok = 0;
|
||||
lua_Number n = lua_tonumberx(L, i, &ok);
|
||||
if (ok) {
|
||||
if (n == (lua_Integer)n) {
|
||||
if (isnum)
|
||||
*isnum = 1;
|
||||
return (lua_Integer)n;
|
||||
}
|
||||
}
|
||||
if (isnum)
|
||||
*isnum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void compat53_reverse (lua_State *L, int a, int b) {
|
||||
for (; a < b; ++a, --b) {
|
||||
lua_pushvalue(L, a);
|
||||
lua_pushvalue(L, b);
|
||||
lua_replace(L, a);
|
||||
lua_replace(L, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) {
|
||||
int n_elems = 0;
|
||||
idx = lua_absindex(L, idx);
|
||||
n_elems = lua_gettop(L)-idx+1;
|
||||
if (n < 0)
|
||||
n += n_elems;
|
||||
if ( n > 0 && n < n_elems) {
|
||||
luaL_checkstack(L, 2, "not enough stack slots available");
|
||||
n = n_elems - n;
|
||||
compat53_reverse(L, idx, idx+n-1);
|
||||
compat53_reverse(L, idx+n, idx+n_elems-1);
|
||||
compat53_reverse(L, idx, idx+n_elems-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) {
|
||||
luaL_checkstack(L, 1, "not enough stack slots available");
|
||||
index = lua_absindex(L, index);
|
||||
lua_pushinteger(L, i);
|
||||
lua_insert(L, -2);
|
||||
lua_settable(L, index);
|
||||
}
|
||||
|
||||
|
||||
#if !defined(lua_str2number)
|
||||
# define lua_str2number(s, p) strtod((s), (p))
|
||||
#endif
|
||||
|
||||
COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) {
|
||||
char* endptr;
|
||||
lua_Number n = lua_str2number(s, &endptr);
|
||||
if (endptr != s) {
|
||||
while (*endptr != '\0' && isspace((unsigned char)*endptr))
|
||||
++endptr;
|
||||
if (*endptr == '\0') {
|
||||
lua_pushnumber(L, n);
|
||||
return endptr - s + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
if (!luaL_callmeta(L, idx, "__tostring")) {
|
||||
int t = lua_type(L, idx), tt = 0;
|
||||
char const* name = NULL;
|
||||
switch (t) {
|
||||
case LUA_TNIL:
|
||||
lua_pushliteral(L, "nil");
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
case LUA_TNUMBER:
|
||||
lua_pushvalue(L, idx);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, idx))
|
||||
lua_pushliteral(L, "true");
|
||||
else
|
||||
lua_pushliteral(L, "false");
|
||||
break;
|
||||
default:
|
||||
tt = luaL_getmetafield(L, idx, "__name");
|
||||
name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t);
|
||||
lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx));
|
||||
if (tt != LUA_TNIL)
|
||||
lua_replace(L, -2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
}
|
||||
return lua_tolstring(L, -1, len);
|
||||
}
|
||||
|
||||
|
||||
COMPAT53_API void luaL_requiref (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb) {
|
||||
luaL_checkstack(L, 3, "not enough stack slots available");
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
|
||||
if (lua_getfield(L, -1, modname) == LUA_TNIL) {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
lua_pushstring(L, modname);
|
||||
#ifndef COMPAT53_LUAJIT
|
||||
lua_call(L, 1, 1);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, modname);
|
||||
#else
|
||||
lua_call(L, 1, 0);
|
||||
lua_getfield(L, -1, modname);
|
||||
#endif /* COMPAT53_LUAJIT */
|
||||
}
|
||||
if (glb) {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, modname);
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
}
|
||||
|
||||
|
||||
#endif /* Lua 5.1 and 5.2 */
|
||||
|
||||
|
||||
#endif /* COMPAT53_C_ */
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* This file contains parts of Lua 5.2's and Lua 5.3's source code:
|
||||
*
|
||||
* Copyright (C) 1994-2014 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
#ifndef COMPAT53_H_
|
||||
#define COMPAT53_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#undef COMPAT53_INCLUDE_SOURCE
|
||||
#if defined(COMPAT53_PREFIX)
|
||||
/* - change the symbol names of functions to avoid linker conflicts
|
||||
* - compat-5.3.c needs to be compiled (and linked) separately
|
||||
*/
|
||||
# if !defined(COMPAT53_API)
|
||||
# define COMPAT53_API extern
|
||||
# endif
|
||||
#else /* COMPAT53_PREFIX */
|
||||
/* - make all functions static and include the source.
|
||||
* - compat-5.3.c doesn't need to be compiled (and linked) separately
|
||||
*/
|
||||
# define COMPAT53_PREFIX compat53
|
||||
# undef COMPAT53_API
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define COMPAT53_API __attribute__((__unused__)) static
|
||||
# else
|
||||
# define COMPAT53_API static
|
||||
# endif
|
||||
# define COMPAT53_INCLUDE_SOURCE
|
||||
#endif /* COMPAT53_PREFIX */
|
||||
|
||||
#define COMPAT53_CONCAT_HELPER(a, b) a##b
|
||||
#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b)
|
||||
|
||||
|
||||
|
||||
/* declarations for Lua 5.1 */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||
|
||||
/* XXX not implemented:
|
||||
* lua_arith (new operators)
|
||||
* lua_upvalueid
|
||||
* lua_upvaluejoin
|
||||
* lua_version
|
||||
* lua_yieldk
|
||||
*/
|
||||
|
||||
#ifndef LUA_OK
|
||||
# define LUA_OK 0
|
||||
#endif
|
||||
#ifndef LUA_OPADD
|
||||
# define LUA_OPADD 0
|
||||
#endif
|
||||
#ifndef LUA_OPSUB
|
||||
# define LUA_OPSUB 1
|
||||
#endif
|
||||
#ifndef LUA_OPMUL
|
||||
# define LUA_OPMUL 2
|
||||
#endif
|
||||
#ifndef LUA_OPDIV
|
||||
# define LUA_OPDIV 3
|
||||
#endif
|
||||
#ifndef LUA_OPMOD
|
||||
# define LUA_OPMOD 4
|
||||
#endif
|
||||
#ifndef LUA_OPPOW
|
||||
# define LUA_OPPOW 5
|
||||
#endif
|
||||
#ifndef LUA_OPUNM
|
||||
# define LUA_OPUNM 6
|
||||
#endif
|
||||
#ifndef LUA_OPEQ
|
||||
# define LUA_OPEQ 0
|
||||
#endif
|
||||
#ifndef LUA_OPLT
|
||||
# define LUA_OPLT 1
|
||||
#endif
|
||||
#ifndef LUA_OPLE
|
||||
# define LUA_OPLE 2
|
||||
#endif
|
||||
|
||||
/* LuaJIT/Lua 5.1 does not have the updated
|
||||
* error codes for thread status/function returns (but some patched versions do)
|
||||
* define it only if it's not found
|
||||
*/
|
||||
#if !defined(LUA_ERRGCMM)
|
||||
/* Use + 2 because in some versions of Lua (Lua 5.1)
|
||||
* LUA_ERRFILE is defined as (LUA_ERRERR+1)
|
||||
* so we need to avoid it (LuaJIT might have something at this
|
||||
* integer value too)
|
||||
*/
|
||||
# define LUA_ERRGCMM (LUA_ERRERR + 2)
|
||||
#endif /* LUA_ERRGCMM define */
|
||||
|
||||
typedef size_t lua_Unsigned;
|
||||
|
||||
typedef struct luaL_Buffer_53 {
|
||||
luaL_Buffer b; /* make incorrect code crash! */
|
||||
char *ptr;
|
||||
size_t nelems;
|
||||
size_t capacity;
|
||||
lua_State *L2;
|
||||
} luaL_Buffer_53;
|
||||
#define luaL_Buffer luaL_Buffer_53
|
||||
|
||||
/* In PUC-Rio 5.1, userdata is a simple FILE*
|
||||
* In LuaJIT, it's a struct where the first member is a FILE*
|
||||
* We can't support the `closef` member
|
||||
*/
|
||||
typedef struct luaL_Stream {
|
||||
FILE *f;
|
||||
} luaL_Stream;
|
||||
|
||||
#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex)
|
||||
COMPAT53_API int lua_absindex (lua_State *L, int i);
|
||||
|
||||
#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith)
|
||||
COMPAT53_API void lua_arith (lua_State *L, int op);
|
||||
|
||||
#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare)
|
||||
COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op);
|
||||
|
||||
#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy)
|
||||
COMPAT53_API void lua_copy (lua_State *L, int from, int to);
|
||||
|
||||
#define lua_getuservalue(L, i) \
|
||||
(lua_getfenv((L), (i)), lua_type((L), -1))
|
||||
#define lua_setuservalue(L, i) \
|
||||
(luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i)))
|
||||
|
||||
#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len)
|
||||
COMPAT53_API void lua_len (lua_State *L, int i);
|
||||
|
||||
#define lua_pushstring(L, s) \
|
||||
(lua_pushstring((L), (s)), lua_tostring((L), -1))
|
||||
|
||||
#define lua_pushlstring(L, s, len) \
|
||||
((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1))
|
||||
|
||||
#ifndef luaL_newlibtable
|
||||
# define luaL_newlibtable(L, l) \
|
||||
(lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1))
|
||||
#endif
|
||||
#ifndef luaL_newlib
|
||||
# define luaL_newlib(L, l) \
|
||||
(luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l)))
|
||||
#endif
|
||||
|
||||
#define lua_pushglobaltable(L) \
|
||||
lua_pushvalue((L), LUA_GLOBALSINDEX)
|
||||
|
||||
#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp)
|
||||
COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p);
|
||||
|
||||
#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp)
|
||||
COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p);
|
||||
|
||||
#define lua_rawlen(L, i) lua_objlen((L), (i))
|
||||
|
||||
#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL)
|
||||
|
||||
#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx)
|
||||
COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum);
|
||||
|
||||
#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion)
|
||||
COMPAT53_API void luaL_checkversion (lua_State *L);
|
||||
|
||||
#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
|
||||
COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode);
|
||||
|
||||
#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex)
|
||||
COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode);
|
||||
|
||||
#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx)
|
||||
COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
|
||||
|
||||
#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53)
|
||||
COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg);
|
||||
|
||||
#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable)
|
||||
COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name);
|
||||
|
||||
#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len)
|
||||
COMPAT53_API lua_Integer luaL_len (lua_State *L, int i);
|
||||
|
||||
#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs)
|
||||
COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
|
||||
|
||||
#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable)
|
||||
COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname);
|
||||
|
||||
#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata)
|
||||
COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname);
|
||||
|
||||
#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback)
|
||||
COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level);
|
||||
|
||||
#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult)
|
||||
COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname);
|
||||
|
||||
#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult)
|
||||
COMPAT53_API int luaL_execresult (lua_State *L, int stat);
|
||||
|
||||
#define lua_callk(L, na, nr, ctx, cont) \
|
||||
((void)(ctx), (void)(cont), lua_call((L), (na), (nr)))
|
||||
#define lua_pcallk(L, na, nr, err, ctx, cont) \
|
||||
((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err)))
|
||||
|
||||
#define lua_resume(L, from, nargs) \
|
||||
((void)(from), lua_resume((L), (nargs)))
|
||||
|
||||
#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53)
|
||||
COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B);
|
||||
|
||||
#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53)
|
||||
COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s);
|
||||
|
||||
#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53)
|
||||
COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l);
|
||||
|
||||
#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53)
|
||||
COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B);
|
||||
|
||||
#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53)
|
||||
COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B);
|
||||
|
||||
#undef luaL_buffinitsize
|
||||
#define luaL_buffinitsize(L, B, s) \
|
||||
(luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s)))
|
||||
|
||||
#undef luaL_prepbuffer
|
||||
#define luaL_prepbuffer(B) \
|
||||
luaL_prepbuffsize((B), LUAL_BUFFERSIZE)
|
||||
|
||||
#undef luaL_addchar
|
||||
#define luaL_addchar(B, c) \
|
||||
((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \
|
||||
((B)->ptr[(B)->nelems++] = (c)))
|
||||
|
||||
#undef luaL_addsize
|
||||
#define luaL_addsize(B, s) \
|
||||
((B)->nelems += (s))
|
||||
|
||||
#undef luaL_addstring
|
||||
#define luaL_addstring(B, s) \
|
||||
luaL_addlstring((B), (s), strlen((s)))
|
||||
|
||||
#undef luaL_pushresultsize
|
||||
#define luaL_pushresultsize(B, s) \
|
||||
(luaL_addsize((B), (s)), luaL_pushresult((B)))
|
||||
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
#define lua_pushunsigned(L, n) \
|
||||
lua_pushinteger((L), (lua_Integer)(n))
|
||||
#define lua_tounsignedx(L, i, is) \
|
||||
((lua_Unsigned)lua_tointegerx((L), (i), (is)))
|
||||
#define lua_tounsigned(L, i) \
|
||||
lua_tounsignedx((L), (i), NULL)
|
||||
#define luaL_checkunsigned(L, a) \
|
||||
((lua_Unsigned)luaL_checkinteger((L), (a)))
|
||||
#define luaL_optunsigned(L, a, d) \
|
||||
((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d)))
|
||||
#endif
|
||||
|
||||
#endif /* Lua 5.1 only */
|
||||
|
||||
|
||||
|
||||
/* declarations for Lua 5.1 and 5.2 */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
|
||||
|
||||
typedef int lua_KContext;
|
||||
|
||||
typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx);
|
||||
|
||||
#define lua_dump(L, w, d, s) \
|
||||
((void)(s), lua_dump((L), (w), (d)))
|
||||
|
||||
#define lua_getfield(L, i, k) \
|
||||
(lua_getfield((L), (i), (k)), lua_type((L), -1))
|
||||
|
||||
#define lua_gettable(L, i) \
|
||||
(lua_gettable((L), (i)), lua_type((L), -1))
|
||||
|
||||
#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti)
|
||||
COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i);
|
||||
|
||||
#define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace)
|
||||
COMPAT53_API void *lua_getextraspace (lua_State *L);
|
||||
|
||||
#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger)
|
||||
COMPAT53_API int lua_isinteger (lua_State *L, int index);
|
||||
|
||||
#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53)
|
||||
COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum);
|
||||
|
||||
#define lua_numbertointeger(n, p) \
|
||||
((*(p) = (lua_Integer)(n)), 1)
|
||||
|
||||
#define lua_rawget(L, i) \
|
||||
(lua_rawget((L), (i)), lua_type((L), -1))
|
||||
|
||||
#define lua_rawgeti(L, i, n) \
|
||||
(lua_rawgeti((L), (i), (n)), lua_type((L), -1))
|
||||
|
||||
#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate)
|
||||
COMPAT53_API void lua_rotate (lua_State *L, int idx, int n);
|
||||
|
||||
#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti)
|
||||
COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i);
|
||||
|
||||
#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber)
|
||||
COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s);
|
||||
|
||||
#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring)
|
||||
COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len);
|
||||
|
||||
#define luaL_getmetafield(L, o, e) \
|
||||
(luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL)
|
||||
|
||||
#define luaL_newmetatable(L, tn) \
|
||||
(luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0)
|
||||
|
||||
#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53)
|
||||
COMPAT53_API void luaL_requiref (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb );
|
||||
|
||||
#endif /* Lua 5.1 and Lua 5.2 */
|
||||
|
||||
|
||||
|
||||
/* declarations for Lua 5.2 */
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502
|
||||
|
||||
/* XXX not implemented:
|
||||
* lua_isyieldable
|
||||
* lua_arith (new operators)
|
||||
* lua_pushfstring (new formats)
|
||||
*/
|
||||
|
||||
#define lua_getglobal(L, n) \
|
||||
(lua_getglobal((L), (n)), lua_type((L), -1))
|
||||
|
||||
#define lua_getuservalue(L, i) \
|
||||
(lua_getuservalue((L), (i)), lua_type((L), -1))
|
||||
|
||||
#define lua_pushlstring(L, s, len) \
|
||||
(((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len)))
|
||||
|
||||
#define lua_rawgetp(L, i, p) \
|
||||
(lua_rawgetp((L), (i), (p)), lua_type((L), -1))
|
||||
|
||||
#define LUA_KFUNCTION(_name) \
|
||||
static int (_name)(lua_State *L, int status, lua_KContext ctx); \
|
||||
static int (_name ## _52)(lua_State *L) { \
|
||||
lua_KContext ctx; \
|
||||
int status = lua_getctx(L, &ctx); \
|
||||
return (_name)(L, status, ctx); \
|
||||
} \
|
||||
static int (_name)(lua_State *L, int status, lua_KContext ctx)
|
||||
|
||||
#define lua_pcallk(L, na, nr, err, ctx, cont) \
|
||||
lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52)
|
||||
|
||||
#define lua_callk(L, na, nr, ctx, cont) \
|
||||
lua_callk((L), (na), (nr), (ctx), cont ## _52)
|
||||
|
||||
#define lua_yieldk(L, nr, ctx, cont) \
|
||||
lua_yieldk((L), (nr), (ctx), cont ## _52)
|
||||
|
||||
#ifdef lua_call
|
||||
# undef lua_call
|
||||
# define lua_call(L, na, nr) \
|
||||
(lua_callk)((L), (na), (nr), 0, NULL)
|
||||
#endif
|
||||
|
||||
#ifdef lua_pcall
|
||||
# undef lua_pcall
|
||||
# define lua_pcall(L, na, nr, err) \
|
||||
(lua_pcallk)((L), (na), (nr), (err), 0, NULL)
|
||||
#endif
|
||||
|
||||
#ifdef lua_yield
|
||||
# undef lua_yield
|
||||
# define lua_yield(L, nr) \
|
||||
(lua_yieldk)((L), (nr), 0, NULL)
|
||||
#endif
|
||||
|
||||
#endif /* Lua 5.2 only */
|
||||
|
||||
|
||||
|
||||
/* other Lua versions */
|
||||
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504
|
||||
|
||||
# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)"
|
||||
|
||||
#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */
|
||||
|
||||
|
||||
|
||||
/* helper macro for defining continuation functions (for every version
|
||||
* *except* Lua 5.2) */
|
||||
#ifndef LUA_KFUNCTION
|
||||
#define LUA_KFUNCTION(_name) \
|
||||
static int (_name)(lua_State *L, int status, lua_KContext ctx)
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(COMPAT53_INCLUDE_SOURCE)
|
||||
# include "compat-5.3.c"
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* COMPAT53_H_ */
|
||||
|
||||
@@ -1,509 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include <lauxlib.h>
|
||||
#include <lua.h>
|
||||
|
||||
#include "compat-5.3.h"
|
||||
|
||||
size_t MLUA_WRAPPED_ERROR_SIZE = 0;
|
||||
size_t MLUA_WRAPPED_PANIC_SIZE = 0;
|
||||
|
||||
const void *MLUA_WRAPPED_ERROR_KEY = NULL;
|
||||
const void *MLUA_WRAPPED_PANIC_KEY = NULL;
|
||||
|
||||
extern void wrapped_error_traceback(lua_State *L, int error_idx, void *error_ud,
|
||||
int has_traceback);
|
||||
|
||||
extern int mlua_hook_proc(lua_State *L, lua_Debug *ar);
|
||||
|
||||
#define max(a, b) (a > b ? a : b)
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
size_t len;
|
||||
} StringArg;
|
||||
|
||||
// A wrapper around Rust function to protect from triggering longjmp in Rust.
|
||||
// Rust callback expected to return -1 in case of errors or number of output
|
||||
// values.
|
||||
static int lua_call_rust(lua_State *L) {
|
||||
int nargs = lua_gettop(L);
|
||||
|
||||
// We need one extra stack space to store preallocated memory, and at least 2
|
||||
// stack spaces overall for handling error metatables in rust fn
|
||||
int extra_stack = 1;
|
||||
if (nargs < 2) {
|
||||
extra_stack = 2 - nargs;
|
||||
}
|
||||
|
||||
luaL_checkstack(L, extra_stack,
|
||||
"not enough stack space for callback error handling");
|
||||
|
||||
// We cannot shadow rust errors with Lua ones, we pre-allocate enough memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE));
|
||||
lua_rotate(L, 1, 1);
|
||||
|
||||
lua_CFunction rust_callback = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
int ret = rust_callback(L);
|
||||
if (ret == -1) {
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lua_call_mlua_hook_proc(lua_State *L, lua_Debug *ar) {
|
||||
luaL_checkstack(L, 2, "not enough stack space for callback error handling");
|
||||
lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE));
|
||||
lua_rotate(L, 1, 1);
|
||||
int ret = mlua_hook_proc(L, ar);
|
||||
if (ret == -1) {
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
|
||||
static inline lua_Integer lua_popinteger(lua_State *L) {
|
||||
lua_Integer index = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return index;
|
||||
}
|
||||
|
||||
//
|
||||
// Common functions
|
||||
//
|
||||
|
||||
int lua_gc_s(lua_State *L) {
|
||||
int data = lua_popinteger(L);
|
||||
int what = lua_popinteger(L);
|
||||
int ret = lua_gc(L, what, data);
|
||||
lua_pushinteger(L, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaL_ref_s(lua_State *L) {
|
||||
int ret = luaL_ref(L, -2);
|
||||
lua_pushinteger(L, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_pushlstring_s(lua_State *L) {
|
||||
StringArg *s = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushlstring(L, s->data, s->len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_tolstring_s(lua_State *L) {
|
||||
void *len = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
const char *s = lua_tolstring(L, -1, len);
|
||||
lua_pushlightuserdata(L, (void *)s);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int lua_newthread_s(lua_State *L) {
|
||||
lua_newthread(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_newuserdata_s(lua_State *L) {
|
||||
size_t size = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_newuserdata(L, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_newwrappederror_s(lua_State *L) {
|
||||
lua_newuserdata(L, MLUA_WRAPPED_ERROR_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_pushcclosure_s(lua_State *L) {
|
||||
int n = lua_gettop(L) - 1;
|
||||
lua_CFunction fn = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushcclosure(L, fn, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_pushrclosure_s(lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
lua_pushcclosure(L, lua_call_rust, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaL_requiref_s(lua_State *L) {
|
||||
const char *modname = lua_touserdata(L, -3);
|
||||
lua_CFunction openf = lua_touserdata(L, -2);
|
||||
int glb = lua_tointeger(L, -1);
|
||||
lua_pop(L, 3);
|
||||
luaL_requiref(L, modname, openf, glb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Table functions
|
||||
//
|
||||
|
||||
int lua_newtable_s(lua_State *L) {
|
||||
lua_createtable(L, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_createtable_s(lua_State *L) {
|
||||
int nrec = lua_popinteger(L);
|
||||
int narr = lua_popinteger(L);
|
||||
lua_createtable(L, narr, nrec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_gettable_s(lua_State *L) {
|
||||
lua_gettable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_settable_s(lua_State *L) {
|
||||
lua_settable(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_geti_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_geti(L, -1, index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_rawset_s(lua_State *L) {
|
||||
lua_rawset(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawseti_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_rawseti(L, -2, index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawsetp_s(lua_State *L) {
|
||||
void *p = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_rawsetp(L, -2, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawsetfield_s(lua_State *L) {
|
||||
StringArg *s = lua_touserdata(L, -2);
|
||||
lua_pushlstring(L, s->data, s->len);
|
||||
lua_replace(L, -3);
|
||||
lua_rawset(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawinsert_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_Integer size = lua_rawlen(L, -2);
|
||||
|
||||
for (lua_Integer i = size; i >= index; i--) {
|
||||
// table[i+1] = table[i]
|
||||
lua_rawgeti(L, -2, i);
|
||||
lua_rawseti(L, -3, i + 1);
|
||||
}
|
||||
lua_rawseti(L, -2, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua_rawremove_s(lua_State *L) {
|
||||
lua_Integer index = lua_popinteger(L);
|
||||
lua_Integer size = lua_rawlen(L, -1);
|
||||
|
||||
for (lua_Integer i = index; i < size; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
lua_rawseti(L, -2, i);
|
||||
}
|
||||
lua_pushnil(L);
|
||||
lua_rawseti(L, -2, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luaL_len_s(lua_State *L) {
|
||||
lua_pushinteger(L, luaL_len(L, -1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_next_s(lua_State *L) {
|
||||
int ret = lua_next(L, -2);
|
||||
lua_pushinteger(L, ret);
|
||||
return ret == 0 ? 1 : 3;
|
||||
}
|
||||
|
||||
//
|
||||
// Moved from Rust to C
|
||||
//
|
||||
|
||||
// Wrapper to lookup in `field_getters` first, then `methods`, ending
|
||||
// original `__index`. Used only if `field_getters` or `methods` set.
|
||||
int meta_index_impl(lua_State *state) {
|
||||
// stack: self, key
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
// lookup in `field_getters` table
|
||||
if (lua_isnil(state, lua_upvalueindex(2)) == 0) {
|
||||
lua_pushvalue(state, -1); // `key` arg
|
||||
if (lua_rawget(state, lua_upvalueindex(2)) != LUA_TNIL) {
|
||||
lua_insert(state, -3); // move function
|
||||
lua_pop(state, 1); // remove `key`
|
||||
lua_call(state, 1, 1);
|
||||
return 1;
|
||||
}
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
}
|
||||
// lookup in `methods` table
|
||||
if (lua_isnil(state, lua_upvalueindex(3)) == 0) {
|
||||
lua_pushvalue(state, -1); // `key` arg
|
||||
if (lua_rawget(state, lua_upvalueindex(3)) != LUA_TNIL) {
|
||||
lua_insert(state, -3);
|
||||
lua_pop(state, 2);
|
||||
return 1;
|
||||
}
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
}
|
||||
|
||||
// lookup in `__index`
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
switch (lua_type(state, -1)) {
|
||||
case LUA_TNIL:
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
const char *field = lua_tostring(state, -1);
|
||||
luaL_error(state, "attempt to get an unknown field '%s'", field);
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
lua_insert(state, -2);
|
||||
lua_gettable(state, -2);
|
||||
break;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
lua_insert(state, -3);
|
||||
lua_call(state, 2, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Similar to `meta_index_impl`, checks `field_setters` table first, then
|
||||
// `__newindex` metamethod. Used only if `field_setters` set.
|
||||
int meta_newindex_impl(lua_State *state) {
|
||||
// stack: self, key, value
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
// lookup in `field_setters` table
|
||||
lua_pushvalue(state, -2); // `key` arg
|
||||
if (lua_rawget(state, lua_upvalueindex(2)) != LUA_TNIL) {
|
||||
lua_remove(state, -3); // remove `key`
|
||||
lua_insert(state, -3); // move function
|
||||
lua_call(state, 2, 0);
|
||||
return 0;
|
||||
}
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
|
||||
// lookup in `__newindex`
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
switch (lua_type(state, -1)) {
|
||||
case LUA_TNIL:
|
||||
lua_pop(state, 1); // pop the nil value
|
||||
const char *field = lua_tostring(state, -2);
|
||||
luaL_error(state, "attempt to set an unknown field '%s'", field);
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
lua_insert(state, -3);
|
||||
lua_settable(state, -3);
|
||||
break;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
lua_insert(state, -4);
|
||||
lua_call(state, 3, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// See Function::bind
|
||||
int bind_call_impl(lua_State *state) {
|
||||
int nargs = lua_gettop(state);
|
||||
int nbinds = lua_tointeger(state, lua_upvalueindex(2));
|
||||
luaL_checkstack(state, nbinds + 2, NULL);
|
||||
|
||||
lua_settop(state, nargs + nbinds + 1);
|
||||
lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
||||
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
lua_replace(state, 1);
|
||||
|
||||
for (int i = 0; i < nbinds; i++) {
|
||||
lua_pushvalue(state, lua_upvalueindex(i + 3));
|
||||
lua_replace(state, i + 2);
|
||||
}
|
||||
|
||||
lua_call(state, nargs + nbinds, LUA_MULTRET);
|
||||
return lua_gettop(state);
|
||||
}
|
||||
|
||||
// Returns 1 if a value at index `index` is a special wrapped struct identified
|
||||
// by `key`
|
||||
int is_wrapped_struct(lua_State *state, int index, const void *key) {
|
||||
if (key == NULL) {
|
||||
// Not yet initialized?
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ud = lua_touserdata(state, index);
|
||||
if (ud == NULL || lua_getmetatable(state, index) == 0) {
|
||||
return 0;
|
||||
}
|
||||
lua_rawgetp(state, LUA_REGISTRYINDEX, key);
|
||||
int res = lua_rawequal(state, -1, -2);
|
||||
lua_pop(state, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Takes an error at the top of the stack, and if it is a WrappedError, converts
|
||||
// it to an Error::CallbackError with a traceback, if it is some lua type,
|
||||
// prints the error along with a traceback, and if it is a WrappedPanic, does
|
||||
// not modify it. This function does its best to avoid triggering another error
|
||||
// and shadowing previous rust errors, but it may trigger Lua errors that shadow
|
||||
// rust errors under certain memory conditions. This function ensures that such
|
||||
// behavior will *never* occur with a rust panic, however.
|
||||
int error_traceback(lua_State *state) {
|
||||
// I believe luaL_traceback < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses luaL_Buffer
|
||||
const int LUA_TRACEBACK_STACK = 11;
|
||||
|
||||
if (lua_checkstack(state, 2) == 0) {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_ERROR_KEY) != 0) {
|
||||
int error_idx = lua_absindex(state, -1);
|
||||
// lua_newuserdata and luaL_traceback may error
|
||||
void *error_ud = lua_newuserdata(state, MLUA_WRAPPED_ERROR_SIZE);
|
||||
int has_traceback = 0;
|
||||
if (lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(state, state, NULL, 0);
|
||||
has_traceback = 1;
|
||||
}
|
||||
wrapped_error_traceback(state, error_idx, error_ud, has_traceback);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (MLUA_WRAPPED_PANIC_KEY != NULL &&
|
||||
!is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY) &&
|
||||
lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
const char *s = luaL_tolstring(state, -1, NULL);
|
||||
luaL_traceback(state, state, s, 0);
|
||||
lua_remove(state, -2);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int error_traceback_s(lua_State *L) {
|
||||
lua_State *L1 = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return error_traceback(L1);
|
||||
}
|
||||
|
||||
// A `pcall` implementation that does not allow Lua to catch Rust panics.
|
||||
// Instead, panics automatically resumed.
|
||||
int lua_nopanic_pcall(lua_State *state) {
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
int top = lua_gettop(state);
|
||||
if (top == 0) {
|
||||
lua_pushstring(state, "not enough arguments to pcall");
|
||||
lua_error(state);
|
||||
}
|
||||
|
||||
if (lua_pcall(state, top - 1, LUA_MULTRET, 0) == LUA_OK) {
|
||||
lua_pushboolean(state, 1);
|
||||
lua_insert(state, 1);
|
||||
return lua_gettop(state);
|
||||
}
|
||||
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) {
|
||||
lua_error(state);
|
||||
}
|
||||
lua_pushboolean(state, 0);
|
||||
lua_insert(state, -2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// A `xpcall` implementation that does not allow Lua to catch Rust panics.
|
||||
// Instead, panics automatically resumed.
|
||||
|
||||
static int xpcall_msgh(lua_State *state) {
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) {
|
||||
return 1;
|
||||
}
|
||||
lua_pushvalue(state, lua_upvalueindex(1));
|
||||
lua_insert(state, 1);
|
||||
lua_call(state, lua_gettop(state) - 1, LUA_MULTRET);
|
||||
return lua_gettop(state);
|
||||
}
|
||||
|
||||
int lua_nopanic_xpcall(lua_State *state) {
|
||||
luaL_checkstack(state, 2, NULL);
|
||||
|
||||
int top = lua_gettop(state);
|
||||
if (top < 2) {
|
||||
lua_pushstring(state, "not enough arguments to xpcall");
|
||||
lua_error(state);
|
||||
}
|
||||
|
||||
lua_pushvalue(state, 2);
|
||||
lua_pushcclosure(state, xpcall_msgh, 1);
|
||||
lua_copy(state, 1, 2);
|
||||
lua_replace(state, 1);
|
||||
|
||||
if (lua_pcall(state, lua_gettop(state) - 2, LUA_MULTRET, 1) == LUA_OK) {
|
||||
lua_pushboolean(state, 1);
|
||||
lua_insert(state, 2);
|
||||
return lua_gettop(state) - 1;
|
||||
}
|
||||
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) {
|
||||
lua_error(state);
|
||||
}
|
||||
lua_pushboolean(state, 0);
|
||||
lua_insert(state, -2);
|
||||
return 2;
|
||||
}
|
||||
+27
-4
@@ -1,10 +1,11 @@
|
||||
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, pop_error, StackGuard};
|
||||
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -65,7 +66,7 @@ impl<'lua> Function<'lua> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushcfunction(lua.state, ffi::safe::error_traceback);
|
||||
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||
let stack_start = ffi::lua_gettop(lua.state);
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args {
|
||||
@@ -90,7 +91,7 @@ impl<'lua> Function<'lua> {
|
||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// Internaly it wraps the function to an [`AsyncThread`].
|
||||
/// Internally it wraps the function to an [`AsyncThread`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
@@ -159,6 +160,26 @@ 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 {
|
||||
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);
|
||||
|
||||
for i in 0..nbinds {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
|
||||
ffi::lua_replace(state, i + 2);
|
||||
}
|
||||
|
||||
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
|
||||
ffi::lua_gettop(state)
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
@@ -177,7 +198,9 @@ impl<'lua> Function<'lua> {
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
ffi::safe::lua_pushcclosure(lua.state, ffi::safe::bind_call_impl, nargs + 2)?;
|
||||
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
|
||||
})?;
|
||||
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
|
||||
+94
-5
@@ -1,5 +1,6 @@
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use crate::ffi::{self, lua_Debug, lua_State};
|
||||
@@ -11,7 +12,7 @@ use crate::util::callback_error;
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentaton][lua_doc].
|
||||
/// found in the [Lua 5.3 documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
||||
@@ -23,6 +24,25 @@ pub struct Debug<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
/// from a function that did a tail call.
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar).event {
|
||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||
ffi::LUA_HOOKLINE => DebugEvent::Line,
|
||||
ffi::LUA_HOOKCOUNT => DebugEvent::Count,
|
||||
event => mlua_panic!("Unknown Lua event code: {}", event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames<'a> {
|
||||
unsafe {
|
||||
@@ -95,6 +115,16 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
Call,
|
||||
Ret,
|
||||
TailCall,
|
||||
Line,
|
||||
Count,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
pub name: Option<&'a [u8]>,
|
||||
@@ -140,6 +170,46 @@ pub struct HookTriggers {
|
||||
}
|
||||
|
||||
impl HookTriggers {
|
||||
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
///
|
||||
/// [`on_calls`]: #structfield.on_calls
|
||||
pub fn on_calls() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
///
|
||||
/// [`on_returns`]: #structfield.on_returns
|
||||
pub fn on_returns() -> Self {
|
||||
HookTriggers {
|
||||
on_returns: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
///
|
||||
/// [`every_line`]: #structfield.every_line
|
||||
pub fn every_line() -> Self {
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
///
|
||||
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
|
||||
pub fn every_nth_instruction(n: u32) -> Self {
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(n),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
pub(crate) fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
@@ -165,8 +235,27 @@ impl HookTriggers {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mlua_hook_proc(state: *mut lua_State, ar: *mut lua_Debug) -> c_int {
|
||||
impl BitOr for HookTriggers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
self.on_calls |= rhs.on_calls;
|
||||
self.on_returns |= rhs.on_returns;
|
||||
self.every_line |= rhs.every_line;
|
||||
if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
|
||||
self.every_nth_instruction = rhs.every_nth_instruction;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOrAssign for HookTriggers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
||||
callback_error(state, |_| {
|
||||
let debug = Debug {
|
||||
ar,
|
||||
@@ -174,7 +263,7 @@ pub unsafe extern "C" fn mlua_hook_proc(state: *mut lua_State, ar: *mut lua_Debu
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
|
||||
let lua = Lua::make_from_ptr(state);
|
||||
let lua = mlua_expect!(Lua::make_from_ptr(state), "cannot make Lua instance");
|
||||
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
|
||||
|
||||
#[allow(clippy::match_wild_err_arm)]
|
||||
@@ -183,7 +272,7 @@ pub unsafe extern "C" fn mlua_hook_proc(state: *mut lua_State, ar: *mut lua_Debu
|
||||
Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"),
|
||||
}?;
|
||||
|
||||
Ok(0)
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+72
-11
@@ -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.6.0-beta.1")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.4")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -98,12 +98,12 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
pub use crate::ffi::lua_State;
|
||||
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
|
||||
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::lua::{Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
@@ -111,26 +111,87 @@ pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
pub use crate::serde::{
|
||||
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
|
||||
};
|
||||
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
// Re-export #[mlua_derive::lua_module].
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[doc(hidden)]
|
||||
pub use mlua_derive::*;
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
///
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
///
|
||||
/// Captured variables are **moved** into the chunk.
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, chunk};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let name = "Rustacean";
|
||||
/// lua.load(chunk! {
|
||||
/// print("hello, " .. $name)
|
||||
/// }).exec()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Syntax issues
|
||||
///
|
||||
/// Since the Rust tokenizer will tokenize Lua code, this imposes some restrictions.
|
||||
/// The main thing to remember is:
|
||||
///
|
||||
/// - Use double quoted strings (`""`) instead of single quoted strings (`''`).
|
||||
///
|
||||
/// (Single quoted strings only work if they contain a single character, since in Rust,
|
||||
/// `'a'` is a character literal).
|
||||
///
|
||||
/// - Using Lua comments `--` is not desirable in **stable** Rust and can have bad side effects.
|
||||
///
|
||||
/// This is because procedural macros have Line/Column information available only in
|
||||
/// **nightly** Rust. Instead, Lua chunks represented as a big single line of code in stable Rust.
|
||||
///
|
||||
/// As workaround, Rust comments `//` can be used.
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals don't work.
|
||||
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
///
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: trait.AsChunk.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
#[cfg(any(feature = "module"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
+812
-295
File diff suppressed because it is too large
Load Diff
+19
-4
@@ -17,19 +17,19 @@ macro_rules! cstr {
|
||||
|
||||
macro_rules! mlua_panic {
|
||||
($msg:expr) => {
|
||||
panic!(bug_msg!($msg));
|
||||
panic!(bug_msg!($msg))
|
||||
};
|
||||
|
||||
($msg:expr,) => {
|
||||
mlua_panic!($msg);
|
||||
mlua_panic!($msg)
|
||||
};
|
||||
|
||||
($msg:expr, $($arg:expr),+) => {
|
||||
panic!(bug_msg!($msg), $($arg),+);
|
||||
panic!(bug_msg!($msg), $($arg),+)
|
||||
};
|
||||
|
||||
($msg:expr, $($arg:expr),+,) => {
|
||||
mlua_panic!($msg, $($arg),+);
|
||||
mlua_panic!($msg, $($arg),+)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,3 +94,18 @@ macro_rules! require_module_feature {
|
||||
compile_error!("Feature `module` must be enabled in the `mlua` crate");
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! protect_lua {
|
||||
($state:expr, $nargs:expr, $nresults:expr, $f:expr) => {
|
||||
crate::util::protect_lua_closure($state, $nargs, $nresults, $f)
|
||||
};
|
||||
|
||||
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
|
||||
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
|
||||
$code;
|
||||
$nresults
|
||||
}
|
||||
|
||||
crate::util::protect_lua_call($state, $nargs, do_call)
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
+5
-1
@@ -9,6 +9,7 @@ pub use crate::{
|
||||
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||
};
|
||||
|
||||
@@ -17,4 +18,7 @@ pub use crate::AsyncThread as LuaAsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{LuaSerdeExt, SerializeOptions as LuaSerializeOptions};
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
+48
-64
@@ -1,8 +1,10 @@
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
@@ -11,20 +13,20 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata,
|
||||
StackGuard,
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
|
||||
take_userdata, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_core::future::Future,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
};
|
||||
|
||||
@@ -222,7 +224,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// use [`Scope::create_userdata`] instead.
|
||||
///
|
||||
/// The main limitation that comes from using non-'static userdata is that the produced userdata
|
||||
/// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for
|
||||
/// will no longer have a `TypeId` associated with it, because `TypeId` can only work for
|
||||
/// 'static types. This means that it is impossible, once the userdata is created, to get a
|
||||
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
|
||||
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
|
||||
@@ -238,7 +240,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'scope + UserData,
|
||||
{
|
||||
let data = UserDataCell::new_arc(data);
|
||||
let data = Rc::new(RefCell::new(data));
|
||||
|
||||
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
||||
// inability to work with the more correct callback type that is universally quantified over
|
||||
@@ -247,8 +249,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
data: UserDataCell<T>,
|
||||
data_ptr: *mut c_void,
|
||||
data: Rc<RefCell<T>>,
|
||||
data_ptr: *const c_void,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||
@@ -262,9 +264,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
if let Some(Value::UserData(ud)) = value {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
check_stack(lua.state, 2)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
if get_userdata(lua.state, -1) == data_ptr {
|
||||
if get_userdata(lua.state, -1) as *const _ == data_ptr {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -276,10 +278,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
NonStaticMethod::Method(method) => {
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let data = data
|
||||
.try_borrow()
|
||||
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
||||
.map_err(|_| Error::UserDataBorrowError)?;
|
||||
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &*data, args)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
@@ -293,7 +292,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut data = data
|
||||
.try_borrow_mut()
|
||||
.map(|cell| RefMut::map(cell, AsMut::as_mut))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
(&mut *method)(lua, &mut *data, args)
|
||||
});
|
||||
@@ -324,32 +322,32 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 13)?;
|
||||
|
||||
push_userdata(lua.state, data.clone())?;
|
||||
let data_ptr = ffi::lua_touserdata(lua.state, -1);
|
||||
|
||||
let data_ptr = protect_lua!(lua.state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>())
|
||||
})?;
|
||||
// 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;
|
||||
ffi::safe::lua_createtable(lua.state, 0, meta_methods_nrec as c_int)?;
|
||||
push_table(lua.state, 0, meta_methods_nrec as c_int)?;
|
||||
|
||||
for (k, m) in ud_methods.meta_methods {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?;
|
||||
rawset_field(lua.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?;
|
||||
rawset_field(lua.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(lua.state, -1);
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
ffi::safe::lua_createtable(lua.state, 0, field_getters_nrec as c_int)?;
|
||||
push_table(lua.state, 0, field_getters_nrec as c_int)?;
|
||||
for (k, m) in ud_fields.field_getters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
@@ -357,11 +355,11 @@ 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 {
|
||||
ffi::safe::lua_createtable(lua.state, 0, field_setters_nrec as c_int)?;
|
||||
push_table(lua.state, 0, field_setters_nrec as c_int)?;
|
||||
for (k, m) in ud_fields.field_setters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
@@ -370,16 +368,16 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let methods_nrec = ud_methods.methods.len();
|
||||
if methods_nrec > 0 {
|
||||
// Create table used for methods lookup
|
||||
ffi::safe::lua_createtable(lua.state, 0, methods_nrec as c_int)?;
|
||||
push_table(lua.state, 0, methods_nrec as c_int)?;
|
||||
for (k, m) in ud_methods.methods {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
init_userdata_metatable::<()>(
|
||||
init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
|
||||
lua.state,
|
||||
metatable_index,
|
||||
field_getters_index,
|
||||
@@ -392,10 +390,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
+ methods_index.map(|_| 1).unwrap_or(0);
|
||||
ffi::lua_pop(lua.state, count);
|
||||
|
||||
let mt_id = ffi::lua_topointer(lua.state, -1);
|
||||
let mt_ptr = ffi::lua_topointer(lua.state, -1);
|
||||
// Write userdata just before attaching metatable with `__gc` metamethod
|
||||
ptr::write(data_ptr as _, UserDataCell::new(data));
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_id as isize);
|
||||
lua.register_userdata_metatable(mt_ptr, None);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = lua.create_table()?;
|
||||
@@ -410,9 +410,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
|
||||
// Deregister metatable
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_id = ffi::lua_topointer(state, -1);
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_id as isize);
|
||||
ud.lua.deregister_userdata_metatable(mt_ptr);
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
@@ -427,7 +427,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
mem::transmute(f)
|
||||
}
|
||||
|
||||
vec![Box::new(seal(take_userdata::<UserDataCell<T>>(state)))]
|
||||
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
|
||||
vec![ud]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
@@ -458,17 +459,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud1 = take_userdata::<Callback>(state);
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud = take_userdata::<CallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 3);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 3);
|
||||
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
vec![Box::new(ud)]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
@@ -508,33 +504,22 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 3);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 3);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(ud1), Box::new(ud2)];
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
|
||||
|
||||
// Finally, get polled future and destroy it
|
||||
f.lua.push_ref(&poll_str.0);
|
||||
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud3 = take_userdata::<LocalBoxFuture<Result<MultiValue>>>(state);
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
data.push(Box::new(ud3));
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 3);
|
||||
let ud4 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 3);
|
||||
data.push(Box::new(ud4));
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
data.push(Box::new(upvalue2));
|
||||
}
|
||||
|
||||
data
|
||||
@@ -665,7 +650,6 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
|
||||
where
|
||||
T: Clone,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
|
||||
+254
-47
@@ -1,19 +1,99 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::{TablePairs, TableSequence};
|
||||
use crate::table::{Table, TablePairs, TableSequence};
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
#[derive(Debug)]
|
||||
pub struct Deserializer<'lua>(Value<'lua>);
|
||||
pub struct Deserializer<'lua> {
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
/// A struct with options to change default deserializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as `Thread`, `UserData`, `LightUserData`
|
||||
/// and `Error` will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_unsupported_types: bool,
|
||||
|
||||
/// If true, an attempt to serialize a recursive table (table that refers to itself)
|
||||
/// will cause an error.
|
||||
/// Otherwise subsequent attempts to serialize the same table will be ignored.
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_recursive_tables: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`deny_unsupported_types`] option.
|
||||
///
|
||||
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
|
||||
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
self.deny_unsupported_types = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`deny_recursive_tables`] option.
|
||||
///
|
||||
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
|
||||
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
Deserializer(value)
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
pub fn new_with_options(value: Value<'lua>, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited: Rc::new(RefCell::new(HashSet::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +105,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
@@ -43,7 +123,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => Err(de::Error::custom("invalid value type")),
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
Err(de::Error::custom(format!(
|
||||
"unsupported value type `{}`",
|
||||
self.value.type_name()
|
||||
)))
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +141,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Nil => visitor.visit_none(),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
_ => visitor.visit_some(self),
|
||||
@@ -69,9 +158,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
let (variant, value) = match self.0 {
|
||||
Value::Table(value) => {
|
||||
let mut iter = value.pairs::<StdString, Value>();
|
||||
let (variant, value, _guard) = match self.value {
|
||||
Value::Table(table) => {
|
||||
let _guard = RecursionGuard::new(&table, &self.visited);
|
||||
|
||||
let mut iter = table.pairs::<StdString, Value>();
|
||||
let (variant, value) = match iter.next() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
@@ -88,13 +179,22 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
(variant, Some(value))
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
return Err(de::Error::custom("bad enum value"));
|
||||
}
|
||||
|
||||
(variant, Some(value), Some(_guard))
|
||||
}
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None),
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
|
||||
_ => return Err(de::Error::custom("bad enum value")),
|
||||
};
|
||||
|
||||
visitor.visit_enum(EnumDeserializer { variant, value })
|
||||
visitor.visit_enum(EnumDeserializer {
|
||||
variant,
|
||||
value,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -102,12 +202,18 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let len = t.raw_len() as usize;
|
||||
let mut deserializer = SeqDeserializer(t.raw_sequence_values());
|
||||
let mut deserializer = SeqDeserializer {
|
||||
seq: t.raw_sequence_values(),
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
let seq = visitor.visit_seq(&mut deserializer)?;
|
||||
if deserializer.0.count() == 0 {
|
||||
if deserializer.seq.count() == 0 {
|
||||
Ok(seq)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
@@ -116,7 +222,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +255,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Table(t) => {
|
||||
let mut deserializer = MapDeserializer::new(t.pairs());
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: t.pairs(),
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
processed: 0,
|
||||
};
|
||||
let map = visitor.visit_map(&mut deserializer)?;
|
||||
let count = deserializer.pairs.count();
|
||||
if count == 0 {
|
||||
@@ -160,7 +277,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +304,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'lua>(TableSequence<'lua, Value<'lua>>);
|
||||
struct SeqDeserializer<'lua> {
|
||||
seq: TableSequence<'lua, Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
@@ -193,14 +317,24 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.0.next() {
|
||||
Some(value) => seed.deserialize(Deserializer(value?)).map(Some),
|
||||
None => Ok(None),
|
||||
loop {
|
||||
match self.seq.next() {
|
||||
Some(value) => {
|
||||
let value = value?;
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
continue;
|
||||
}
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer = Deserializer::from_parts(value, self.options, visited);
|
||||
return seed.deserialize(deserializer).map(Some);
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
match self.0.size_hint() {
|
||||
match self.seq.size_hint() {
|
||||
(lower, Some(upper)) if lower == upper => Some(upper),
|
||||
_ => None,
|
||||
}
|
||||
@@ -210,19 +344,11 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'lua> MapDeserializer<'lua> {
|
||||
fn new(pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>) -> Self {
|
||||
MapDeserializer {
|
||||
pairs,
|
||||
value: None,
|
||||
processed: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
@@ -230,15 +356,23 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let key_de = Deserializer(key);
|
||||
seed.deserialize(key_de).map(Some)
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let key_de = Deserializer::from_parts(key, self.options, visited);
|
||||
return seed.deserialize(key_de).map(Some);
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +381,10 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
}
|
||||
}
|
||||
@@ -263,6 +400,8 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
struct EnumDeserializer<'lua> {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
@@ -274,13 +413,19 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
let variant = self.variant.into_deserializer();
|
||||
let variant_access = VariantDeserializer { value: self.value };
|
||||
let variant_access = VariantDeserializer {
|
||||
value: self.value,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
seed.deserialize(variant).map(|v| (v, variant_access))
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
@@ -301,7 +446,9 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
Some(value) => {
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
|
||||
}
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
@@ -314,7 +461,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_seq(Deserializer(value), visitor),
|
||||
Some(value) => serde::Deserializer::deserialize_seq(
|
||||
Deserializer::from_parts(value, self.options, self.visited),
|
||||
visitor,
|
||||
),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"tuple variant",
|
||||
@@ -327,7 +477,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_map(Deserializer(value), visitor),
|
||||
Some(value) => serde::Deserializer::deserialize_map(
|
||||
Deserializer::from_parts(value, self.options, self.visited),
|
||||
visitor,
|
||||
),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"struct variant",
|
||||
@@ -335,3 +488,57 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds `ptr` to the `visited` map and removes on drop
|
||||
// Used to track recursive tables but allow to traverse same tables multiple times
|
||||
struct RecursionGuard {
|
||||
ptr: *const c_void,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<HashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let ptr = unsafe { table.0.lua.get_ref_ptr(&table.0) };
|
||||
visited.borrow_mut().insert(ptr);
|
||||
RecursionGuard { ptr, visited }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RecursionGuard {
|
||||
fn drop(&mut self) {
|
||||
self.visited.borrow_mut().remove(&self.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks `options` and decides should we emit an error or skip next element
|
||||
fn check_value_if_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &RefCell<HashSet<*const c_void>>,
|
||||
) -> Result<bool> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let lua = table.0.lua;
|
||||
let ptr = unsafe { lua.get_ref_ptr(&table.0) };
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
}
|
||||
return Ok(true); // skip
|
||||
}
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_)
|
||||
if !options.deny_unsupported_types =>
|
||||
{
|
||||
return Ok(true); // skip
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(false) // do not skip
|
||||
}
|
||||
|
||||
+52
-8
@@ -158,6 +158,41 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt, DeserializeOptions};
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize, Debug, PartialEq)]
|
||||
/// struct User {
|
||||
/// name: String,
|
||||
/// age: u8,
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let val = lua.load(r#"{name = "John Smith", age = 20, f = function() end}"#).eval()?;
|
||||
/// let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
/// let u: User = lua.from_value_with(val, options)?;
|
||||
///
|
||||
/// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value_with<T: Deserialize<'lua>>(
|
||||
&'lua self,
|
||||
value: Value<'lua>,
|
||||
options: de::Options,
|
||||
) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
@@ -196,19 +231,28 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
{
|
||||
T::deserialize(de::Deserializer::new(value))
|
||||
}
|
||||
|
||||
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new_with_options(value, options))
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
// Uses 2 stack spaces and calls checkstack.
|
||||
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 0, fn(state) {
|
||||
ffi::lua_createtable(state, 0, 1);
|
||||
|
||||
ffi::safe::lua_createtable(state, 0, 1)?;
|
||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_rawset(state, -3);
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
|
||||
|
||||
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key)
|
||||
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
|
||||
|
||||
+7
-5
@@ -19,7 +19,7 @@ pub struct Serializer<'lua> {
|
||||
options: Options,
|
||||
}
|
||||
|
||||
/// A struct with options to change default serializer behaviour.
|
||||
/// A struct with options to change default serializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
@@ -61,7 +61,7 @@ impl Default for Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Retruns a new instance of `Options` with default parameters.
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
@@ -318,12 +318,14 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 6)?;
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
|
||||
ffi::safe::lua_rawseti(lua.state, -2, len + 1)
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -44,6 +46,28 @@ impl<'lua> String<'lua> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts this string to a [`Cow<str>`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
///
|
||||
/// let s = lua.create_string(b"test\xff")?;
|
||||
/// assert_eq!(s.to_string_lossy(), "test\u{fffd}");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
StdString::from_utf8_lossy(self.as_bytes())
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||
|
||||
+36
-20
@@ -62,12 +62,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 6)?;
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
ffi::safe::lua_settable(lua.state, -3)
|
||||
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,11 +101,11 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::safe::lua_gettable(lua.state, -2)?;
|
||||
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
@@ -119,12 +119,11 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::safe::lua_gettable(lua.state, -2)?;
|
||||
|
||||
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
Ok(ffi::lua_isnil(lua.state, -1) == 0)
|
||||
}
|
||||
}
|
||||
@@ -193,12 +192,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 6)?;
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
ffi::safe::lua_rawset(lua.state, -3)
|
||||
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,21 +231,28 @@ impl<'lua> Table<'lua> {
|
||||
let value = value.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 6)?;
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
ffi::safe::lua_rawinsert(lua.state, -2, idx)
|
||||
protect_lua!(lua.state, 2, 0, |state| {
|
||||
for i in (idx..=size).rev() {
|
||||
// table[i+1] = table[i]
|
||||
ffi::lua_rawgeti(state, -2, i);
|
||||
ffi::lua_rawseti(state, -3, i + 1);
|
||||
}
|
||||
ffi::lua_rawseti(state, -2, idx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in worst case,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For othey key types this is equivalent to setting `table[key] = nil`.
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
@@ -258,10 +264,17 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::safe::lua_rawremove(lua.state, -1, idx)
|
||||
protect_lua!(lua.state, 1, 0, |state| {
|
||||
for i in idx..size {
|
||||
ffi::lua_rawgeti(state, -1, i + 1);
|
||||
ffi::lua_rawseti(state, -2, i);
|
||||
}
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawseti(state, -2, size);
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => self.raw_set(key, Nil),
|
||||
@@ -280,7 +293,7 @@ impl<'lua> Table<'lua> {
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::safe::luaL_len(lua.state, -1)
|
||||
protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +391,7 @@ impl<'lua> Table<'lua> {
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
|
||||
/// encountered. This mirrors the behaviour of Lua's `ipairs` function and will invoke the
|
||||
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
|
||||
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
|
||||
/// metatable will not be called.
|
||||
///
|
||||
@@ -657,7 +670,10 @@ where
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
|
||||
if ffi::safe::lua_next(lua.state, -2)? != 0 {
|
||||
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2)
|
||||
})?;
|
||||
if next != 0 {
|
||||
let value = lua.pop_value();
|
||||
let key = lua.pop_value();
|
||||
Ok(Some((
|
||||
@@ -709,13 +725,13 @@ where
|
||||
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 1 + if self.raw { 0 } else { 4 })?;
|
||||
check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
let res = if self.raw {
|
||||
ffi::lua_rawgeti(lua.state, -1, index)
|
||||
} else {
|
||||
ffi::safe::lua_geti(lua.state, -1, index)?
|
||||
protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
|
||||
};
|
||||
match res {
|
||||
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||
|
||||
+44
-3
@@ -4,9 +4,12 @@ use std::os::raw::c_int;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, check_stack, pop_error, StackGuard};
|
||||
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
@@ -132,7 +135,7 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
ffi::safe::error_traceback2(lua.state, thread_state)?;
|
||||
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
@@ -170,6 +173,43 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets a thread
|
||||
///
|
||||
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
|
||||
/// Returns a error in case of either the original error that stopped the thread or errors
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In [LuaJIT]: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"`
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
|
||||
let ret = ffi::lua_resetthread(lua.state, thread_state);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(lua.state, thread_state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
@@ -218,7 +258,7 @@ impl<'lua> Thread<'lua> {
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let args = args.to_lua_multi(&self.0.lua);
|
||||
let args = args.to_lua_multi(self.0.lua);
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
args0: RefCell::new(Some(args)),
|
||||
@@ -301,6 +341,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((1, Value::LightUserData(ud))) => {
|
||||
|
||||
+40
-2
@@ -1,4 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
@@ -25,10 +26,31 @@ 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>,
|
||||
}
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct AsyncPollUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
@@ -41,6 +63,8 @@ pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdataMT;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
@@ -67,6 +91,20 @@ impl fmt::Debug for RegistryKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.registry_id.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.registry_id == other.registry_id && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
@@ -117,8 +155,8 @@ impl<'lua> PartialEq for LuaRef<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
lua.push_ref(&self);
|
||||
lua.push_ref(&other);
|
||||
lua.push_ref(self);
|
||||
lua.push_ref(other);
|
||||
ffi::lua_rawequal(lua.state, -1, -2) == 1
|
||||
}
|
||||
}
|
||||
|
||||
+150
-137
@@ -1,9 +1,9 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
@@ -11,6 +11,7 @@ use std::future::Future;
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::os::raw::c_void,
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
@@ -19,9 +20,15 @@ use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
@@ -98,6 +105,15 @@ pub enum MetaMethod {
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
Pairs,
|
||||
/// The `__ipairs` metamethod.
|
||||
///
|
||||
/// This is not an operator, but it will be called by the built-in [`ipairs`] function.
|
||||
///
|
||||
/// Requires `feature = "lua52"`
|
||||
///
|
||||
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
IPairs,
|
||||
/// The `__close` metamethod.
|
||||
///
|
||||
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
|
||||
@@ -175,6 +191,8 @@ impl MetaMethod {
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
MetaMethod::Pairs => "__pairs",
|
||||
#[cfg(feature = "lua52")]
|
||||
MetaMethod::IPairs => "__ipairs",
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
MetaMethod::Close => "__close",
|
||||
@@ -235,6 +253,8 @@ impl From<StdString> for MetaMethod {
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
"__pairs" => MetaMethod::Pairs,
|
||||
#[cfg(feature = "lua52")]
|
||||
"__ipairs" => MetaMethod::IPairs,
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close" => MetaMethod::Close,
|
||||
@@ -340,7 +360,6 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
||||
where
|
||||
T: Clone,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
@@ -400,6 +419,20 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_callback(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, _name: Vec<u8>, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
@@ -471,12 +504,24 @@ pub trait UserDataFields<'lua, T: UserData> {
|
||||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_getter(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_setter(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
///
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
||||
/// of [`ToLua`] and [`FromLua`] are automatically provided.
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
||||
/// Implementation of [`ToLua`] is automatically provided, [`FromLua`] is implemented
|
||||
/// only for `T: UserData + Clone`.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -547,93 +592,100 @@ pub trait UserData: Sized {
|
||||
}
|
||||
|
||||
// Wraps UserData in a way to always implement `serde::Serialize` trait.
|
||||
pub(crate) enum UserDataCell<T> {
|
||||
Arc(Arc<RefCell<UserDataWrapped<T>>>),
|
||||
Plain(RefCell<UserDataWrapped<T>>),
|
||||
}
|
||||
pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
UserDataCell::Plain(RefCell::new(UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn new_arc(data: T) -> Self {
|
||||
UserDataCell::Arc(Arc::new(RefCell::new(UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
})))
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataCell::Plain(RefCell::new(UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}))
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new_ser(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value.
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow(&self) -> Result<Ref<T>> {
|
||||
self.0
|
||||
.try_borrow()
|
||||
.map(|r| Ref::map(r, |r| r.deref()))
|
||||
.map_err(|_| Error::UserDataBorrowError)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value.
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_mut(&self) -> Result<RefMut<T>> {
|
||||
self.0
|
||||
.try_borrow_mut()
|
||||
.map(|r| RefMut::map(r, |r| r.deref_mut()))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataCell<T> {
|
||||
type Target = RefCell<UserDataWrapped<T>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => &*t,
|
||||
UserDataCell::Plain(t) => &*t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataCell<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()),
|
||||
UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
pub(crate) enum UserDataWrapped<T> {
|
||||
Default(T),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
Serializable(*mut T, *const dyn erased_serde::Serialize),
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drop(Box::from_raw(self.data));
|
||||
#[cfg(feature = "serialize")]
|
||||
if self.data as *mut () != self.ser as *mut () {
|
||||
drop(Box::from_raw(self.ser));
|
||||
}
|
||||
}
|
||||
impl<T> UserDataWrapped<T> {
|
||||
#[inline]
|
||||
fn new(data: T) -> Self {
|
||||
UserDataWrapped::Default(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.data }
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline]
|
||||
fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped::Serializable(data_raw, data_raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct UserDataSerializeError;
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
if let UserDataWrapped::Serializable(data, _) = *self {
|
||||
drop(unsafe { Box::from_raw(data) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataWrapped<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &**data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &mut **data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
struct UserDataSerializeError;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataSerializeError {
|
||||
@@ -680,26 +732,20 @@ impl<'lua> AnyUserData<'lua> {
|
||||
///
|
||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
|
||||
self.inspect(|cell| {
|
||||
let cell_ref = cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(Ref::map(cell_ref, |x| unsafe { &*x.data }))
|
||||
})
|
||||
self.inspect(|cell| cell.try_borrow())
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
||||
self.inspect(|cell| {
|
||||
let cell_ref = cell
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(RefMut::map(cell_ref, |x| unsafe { &mut *x.data }))
|
||||
})
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
@@ -758,7 +804,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// Returns a metatable of this `UserData`.
|
||||
///
|
||||
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||
/// provides safe access to it methods.
|
||||
/// provides safe access to its methods.
|
||||
///
|
||||
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
@@ -767,31 +813,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
self.get_raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
|
||||
/// Checks for a metamethod in this `AnyUserData`.
|
||||
///
|
||||
/// This function is deprecated and will be removed in v0.7.
|
||||
/// Please use [`get_metatable`] function instead.
|
||||
///
|
||||
/// [`get_metatable`]: #method.get_metatable
|
||||
#[deprecated(
|
||||
since = "0.6.0",
|
||||
note = "Please use the get_metatable function instead"
|
||||
)]
|
||||
pub fn has_metamethod(&self, method: MetaMethod) -> Result<bool> {
|
||||
match self.get_raw_metatable() {
|
||||
Ok(mt) => {
|
||||
let name = self.0.lua.create_string(method.validate()?.name())?;
|
||||
if let Value::Nil = mt.raw_get(name)? {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
Err(Error::UserDataTypeMismatch) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw_metatable(&self) -> Result<Table<'lua>> {
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
@@ -833,25 +854,14 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
check_stack(lua.state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
lua.push_userdata_metatable::<T>()?;
|
||||
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||
// Maybe UserData destructed?
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
get_destructed_userdata_metatable(lua.state);
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 1 {
|
||||
Err(Error::UserDataDestructed)
|
||||
} else {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
let type_id = lua.push_userdata_ref(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -1))
|
||||
}
|
||||
} else {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -950,17 +960,20 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let res = (|| unsafe {
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
||||
(*ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?.ser)
|
||||
.serialize(serializer)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))
|
||||
})();
|
||||
res.map_err(ser::Error::custom)
|
||||
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
|
||||
let data =
|
||||
ud.0.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
match *data {
|
||||
UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
|
||||
UserDataWrapped::Serializable(_, ser) => (&*ser).serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+488
-210
@@ -1,9 +1,10 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -11,13 +12,17 @@ use once_cell::sync::Lazy;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
|
||||
static METATABLE_CACHE: Lazy<Mutex<HashMap<TypeId, u8>>> = Lazy::new(|| {
|
||||
// The capacity must(!) be greater than number of stored keys
|
||||
Mutex::new(HashMap::with_capacity(32))
|
||||
static METATABLE_CACHE: Lazy<HashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = HashMap::with_capacity(32);
|
||||
crate::lua::init_metatable_cache(&mut map);
|
||||
map.insert(TypeId::of::<WrappedFailure>(), 0);
|
||||
map.insert(TypeId::of::<String>(), 0);
|
||||
map
|
||||
});
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
#[inline]
|
||||
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
|
||||
// when there is a way to be confident about stack safety and test it, this could be enabled
|
||||
@@ -29,6 +34,7 @@ pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
}
|
||||
|
||||
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
|
||||
#[inline]
|
||||
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
||||
if ffi::lua_checkstack(state, amount) == 0 {
|
||||
Err(Error::StackError)
|
||||
@@ -44,9 +50,10 @@ pub struct StackGuard {
|
||||
}
|
||||
|
||||
impl StackGuard {
|
||||
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
|
||||
// Creates a StackGuard instance with record of the stack size, and on Drop will check the
|
||||
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
||||
// the beginning, this is considered a fatal logic error and will result in a panic.
|
||||
#[inline]
|
||||
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
@@ -56,6 +63,7 @@ impl StackGuard {
|
||||
}
|
||||
|
||||
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
|
||||
#[inline]
|
||||
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
@@ -85,16 +93,17 @@ impl Drop for StackGuard {
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
|
||||
// always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack.
|
||||
// Provided function must *never* panic.
|
||||
pub unsafe fn protect_lua(
|
||||
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
|
||||
// should not contain any values that implements Drop.
|
||||
// Internally uses 2 extra stack spaces, and does not call checkstack.
|
||||
pub unsafe fn protect_lua_call(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int, // Must be "C-unwind" after stabilizing
|
||||
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
|
||||
) -> Result<()> {
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
ffi::lua_pushcfunction(state, ffi::safe::error_traceback);
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, f);
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
@@ -110,6 +119,78 @@ pub unsafe fn protect_lua(
|
||||
}
|
||||
}
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
|
||||
// given function return type is not the return value count, instead the inner function return
|
||||
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
|
||||
// will generally be lonjmping, should not contain any values that implements Drop.
|
||||
// Internally uses 3 extra stack spaces, and does not call checkstack.
|
||||
pub unsafe fn protect_lua_closure<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
nresults: c_int,
|
||||
f: F,
|
||||
) -> Result<R>
|
||||
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>,
|
||||
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,
|
||||
{
|
||||
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
(*params).result.init = ((*params).function)(state);
|
||||
|
||||
if (*params).nresults == ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
(*params).nresults
|
||||
}
|
||||
}
|
||||
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
|
||||
let mut params = Params {
|
||||
function: f,
|
||||
result: URes { uninit: () },
|
||||
nresults,
|
||||
};
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
||||
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
||||
ffi::lua_remove(state, stack_start + 1);
|
||||
|
||||
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)
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
}
|
||||
|
||||
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
|
||||
// error at the top of the stack:
|
||||
// 1) If the error is actually a WrappedPanic, this will continue the panic.
|
||||
@@ -122,52 +203,92 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
"pop_error called with non-error return code"
|
||||
);
|
||||
|
||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
||||
if let Some(p) = (*panic).0.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
Error::PreviouslyResumedPanic
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
}
|
||||
} else {
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
Some(WrappedFailure::Panic(panic)) => {
|
||||
if let Some(p) = panic.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
Error::PreviouslyResumedPanic
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
match err_code {
|
||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||
ffi::LUA_ERRSYNTAX => {
|
||||
Error::SyntaxError {
|
||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||
// stock Lua REPL does.
|
||||
incomplete_input: err_string.ends_with("<eof>")
|
||||
|| err_string.ends_with("'<eof>'"),
|
||||
message: err_string,
|
||||
match err_code {
|
||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||
ffi::LUA_ERRSYNTAX => {
|
||||
Error::SyntaxError {
|
||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||
// stock Lua REPL does.
|
||||
incomplete_input: err_string.ends_with("<eof>")
|
||||
|| err_string.ends_with("'<eof>'"),
|
||||
message: err_string,
|
||||
}
|
||||
}
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||
_ => mlua_panic!("unrecognized lua error code"),
|
||||
}
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||
_ => mlua_panic!("unrecognized lua error code"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
|
||||
state: *mut ffi::lua_State,
|
||||
s: &S,
|
||||
) -> Result<()> {
|
||||
let s = s.as_ref();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub unsafe fn rawset_field<S>(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
let field = field.as_ref();
|
||||
ffi::lua_pushvalue(state, table);
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
ffi::lua_rotate(state, -3, 2);
|
||||
ffi::lua_rawset(state, -3);
|
||||
})
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<T>())? as *mut T;
|
||||
let ud = protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
})?;
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
|
||||
@@ -194,7 +315,7 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
// 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)?;
|
||||
get_gc_metatable_for::<T>(state);
|
||||
get_gc_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
@@ -205,7 +326,7 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
get_gc_metatable_for::<T>(state);
|
||||
get_gc_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
@@ -229,13 +350,99 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
) -> Result<()> {
|
||||
// Wrapper to lookup in `field_getters` first, then `methods`, ending original `__index`.
|
||||
// Used only if `field_getters` or `methods` set.
|
||||
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
// stack: self, key
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
// lookup in `field_getters` table
|
||||
if ffi::lua_isnil(state, ffi::lua_upvalueindex(2)) == 0 {
|
||||
ffi::lua_pushvalue(state, -1); // `key` arg
|
||||
if ffi::lua_rawget(state, ffi::lua_upvalueindex(2)) != ffi::LUA_TNIL {
|
||||
ffi::lua_insert(state, -3); // move function
|
||||
ffi::lua_pop(state, 1); // remove `key`
|
||||
ffi::lua_call(state, 1, 1);
|
||||
return 1;
|
||||
}
|
||||
ffi::lua_pop(state, 1); // pop the nil value
|
||||
}
|
||||
// lookup in `methods` table
|
||||
if ffi::lua_isnil(state, ffi::lua_upvalueindex(3)) == 0 {
|
||||
ffi::lua_pushvalue(state, -1); // `key` arg
|
||||
if ffi::lua_rawget(state, ffi::lua_upvalueindex(3)) != ffi::LUA_TNIL {
|
||||
ffi::lua_insert(state, -3);
|
||||
ffi::lua_pop(state, 2);
|
||||
return 1;
|
||||
}
|
||||
ffi::lua_pop(state, 1); // pop the nil value
|
||||
}
|
||||
|
||||
// lookup in `__index`
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
match ffi::lua_type(state, -1) {
|
||||
ffi::LUA_TNIL => {
|
||||
ffi::lua_pop(state, 1); // pop the nil value
|
||||
let field = ffi::lua_tostring(state, -1);
|
||||
ffi::luaL_error(state, cstr!("attempt to get an unknown field '%s'"), field);
|
||||
}
|
||||
ffi::LUA_TTABLE => {
|
||||
ffi::lua_insert(state, -2);
|
||||
ffi::lua_gettable(state, -2);
|
||||
}
|
||||
ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_insert(state, -3);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
// Similar to `meta_index_impl`, checks `field_setters` table first, then `__newindex` metamethod.
|
||||
// Used only if `field_setters` set.
|
||||
unsafe extern "C" fn meta_newindex_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
// stack: self, key, value
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
// lookup in `field_setters` table
|
||||
ffi::lua_pushvalue(state, -2); // `key` arg
|
||||
if ffi::lua_rawget(state, ffi::lua_upvalueindex(2)) != ffi::LUA_TNIL {
|
||||
ffi::lua_remove(state, -3); // remove `key`
|
||||
ffi::lua_insert(state, -3); // move function
|
||||
ffi::lua_call(state, 2, 0);
|
||||
return 0;
|
||||
}
|
||||
ffi::lua_pop(state, 1); // pop the nil value
|
||||
|
||||
// lookup in `__newindex`
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
match ffi::lua_type(state, -1) {
|
||||
ffi::LUA_TNIL => {
|
||||
ffi::lua_pop(state, 1); // pop the nil value
|
||||
let field = ffi::lua_tostring(state, -2);
|
||||
ffi::luaL_error(state, cstr!("attempt to set an unknown field '%s'"), field);
|
||||
}
|
||||
ffi::LUA_TTABLE => {
|
||||
ffi::lua_insert(state, -3);
|
||||
ffi::lua_settable(state, -3);
|
||||
}
|
||||
ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_insert(state, -4);
|
||||
ffi::lua_call(state, 3, 0);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
ffi::safe::lua_pushstring(state, "__index")?;
|
||||
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
push_string(state, "__index")?;
|
||||
let index_type = ffi::lua_rawget(state, -2);
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
@@ -245,35 +452,37 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
ffi::safe::lua_pushcclosure(state, ffi::safe::meta_index_impl, 3)?;
|
||||
protect_lua!(state, 3, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, meta_index_impl, 3);
|
||||
})?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
}
|
||||
|
||||
ffi::safe::lua_rawset(state, -3)?;
|
||||
rawset_field(state, -2, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
ffi::safe::lua_pushstring(state, "__newindex")?;
|
||||
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
push_string(state, "__newindex")?;
|
||||
let newindex_type = ffi::lua_rawget(state, -2);
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
ffi::safe::lua_pushcclosure(state, ffi::safe::meta_newindex_impl, 2)?;
|
||||
protect_lua!(state, 2, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, meta_newindex_impl, 2);
|
||||
})?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
}
|
||||
|
||||
ffi::safe::lua_rawset(state, -3)?;
|
||||
rawset_field(state, -2, "__newindex")?;
|
||||
}
|
||||
|
||||
ffi::safe::lua_pushrclosure(state, userdata_destructor::<T>, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__gc")?;
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
rawset_field(state, -2, "__gc")?;
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
@@ -281,29 +490,42 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 1)?;
|
||||
take_userdata::<T>(state);
|
||||
Ok(0)
|
||||
})
|
||||
// It's probably NOT a good idea to catch Rust panics in finalizer
|
||||
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
|
||||
take_userdata::<T>(state);
|
||||
0
|
||||
}
|
||||
|
||||
// In the context of a lua callback, this will call the given function and if the given function
|
||||
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
||||
// longjmp) by a C shim. The error or panic is wrapped in such a way that when calling pop_error back
|
||||
// on the rust side, it will resume the panic (or when popping a panic value from the stack).
|
||||
// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
|
||||
// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
|
||||
// the Rust side, it will resume the panic.
|
||||
//
|
||||
// This function assumes the structure of the stack at the beginning of a callback, that the only
|
||||
// elements on the stack are the arguments to the callback.
|
||||
//
|
||||
// This function uses some of the bottom of the stack for error handling, the given callback will be
|
||||
// given the number of arguments available as an argument, and should return the number of returns
|
||||
// as normal, but cannot assume that the arguments available start at 1.
|
||||
pub unsafe fn callback_error<F>(state: *mut ffi::lua_State, f: F) -> c_int
|
||||
// as normal, but cannot assume that the arguments available start at 0.
|
||||
pub unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
|
||||
where
|
||||
F: FnOnce(c_int) -> Result<c_int>,
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
{
|
||||
let nargs = ffi::lua_gettop(state) - 1;
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
// We need 2 extra stack spaces to store preallocated memory and error/panic metatable
|
||||
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
ffi::lua_remove(state, 1);
|
||||
@@ -311,57 +533,126 @@ where
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
let error_ud = ffi::lua_touserdata(state, 1);
|
||||
ptr::write(error_ud as *mut WrappedError, WrappedError(err));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
|
||||
let wrapped_error = ud as *mut WrappedFailure;
|
||||
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
-1
|
||||
|
||||
// Convert to CallbackError and attach traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
if let WrappedFailure::Error(ref mut err) = *wrapped_error {
|
||||
let cause = Arc::new(err.clone());
|
||||
*err = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
let error_ud = ffi::lua_touserdata(state, 1);
|
||||
ptr::write(error_ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||
get_gc_metatable_for::<WrappedPanic>(state);
|
||||
ptr::write(ud as *mut WrappedFailure, WrappedFailure::Panic(Some(p)));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
-1
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A part of the C shim (error_traceback).
|
||||
// Receives absolute index of error in the stack, a pointer to pre-allocated WrappedError memory,
|
||||
// and optional boolean flag if a traceback value is on top of the stack.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wrapped_error_traceback(
|
||||
state: *mut ffi::lua_State,
|
||||
error_idx: c_int,
|
||||
error_ud: *mut c_void,
|
||||
has_traceback: c_int,
|
||||
) {
|
||||
let error = mlua_expect!(
|
||||
get_wrapped_error(state, error_idx).as_ref(),
|
||||
"cannot get <WrappedError>"
|
||||
);
|
||||
let traceback = if has_traceback != 0 {
|
||||
let traceback = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
if ffi::lua_checkstack(state, 2) == 0 {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1).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, 1);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
||||
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
let top = ffi::lua_gettop(state);
|
||||
if top == 0 {
|
||||
ffi::lua_pushstring(state, cstr!("not enough arguments to pcall"));
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) == ffi::LUA_OK {
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
ffi::lua_insert(state, 1);
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_owned()
|
||||
};
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
{
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_insert(state, -2);
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
let error = error.clone();
|
||||
ffi::lua_remove(state, -2); // Remove original error
|
||||
// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
||||
pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
ptr::write(
|
||||
error_ud as *mut WrappedError,
|
||||
WrappedError(Error::CallbackError {
|
||||
traceback,
|
||||
cause: Arc::new(error),
|
||||
}),
|
||||
);
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
{
|
||||
1
|
||||
} else {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
ffi::lua_insert(state, 1);
|
||||
ffi::lua_call(state, ffi::lua_gettop(state) - 1, ffi::LUA_MULTRET);
|
||||
ffi::lua_gettop(state)
|
||||
}
|
||||
}
|
||||
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
let top = ffi::lua_gettop(state);
|
||||
if top < 2 {
|
||||
ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall"));
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
ffi::lua_pushvalue(state, 2);
|
||||
ffi::lua_pushcclosure(state, xpcall_msgh, 1);
|
||||
ffi::lua_copy(state, 1, 2);
|
||||
ffi::lua_replace(state, 1);
|
||||
|
||||
if ffi::lua_pcall(state, ffi::lua_gettop(state) - 2, ffi::LUA_MULTRET, 1) == ffi::LUA_OK {
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
ffi::lua_insert(state, 2);
|
||||
ffi::lua_gettop(state) - 1
|
||||
} else {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
{
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_insert(state, -2);
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
||||
@@ -387,75 +678,44 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes a WrappedError to the top of the stack.
|
||||
// Uses 2 stack spaces and does not call checkstack.
|
||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
||||
let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
|
||||
ptr::write(error_ud, WrappedError(err));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
||||
// otherwise returns null.
|
||||
// Uses 2 stack spaces and does not call checkstack.
|
||||
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
||||
let ud = get_gc_userdata::<WrappedError>(state, index);
|
||||
if ud.is_null() {
|
||||
return ptr::null();
|
||||
}
|
||||
&(*ud).0
|
||||
}
|
||||
|
||||
// Initialize the internal (with __gc method) metatable for a type T.
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub unsafe fn init_gc_metatable_for<T: Any>(
|
||||
pub unsafe fn init_gc_metatable<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<*const u8> {
|
||||
) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = {
|
||||
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||
mlua_assert!(
|
||||
mt_cache.capacity() - mt_cache.len() > 0,
|
||||
"out of metatable cache capacity"
|
||||
);
|
||||
mt_cache.insert(type_id, 0);
|
||||
&mt_cache[&type_id] as *const u8
|
||||
};
|
||||
push_table(state, 0, 3)?;
|
||||
|
||||
ffi::safe::lua_createtable(state, 0, 3)?;
|
||||
|
||||
ffi::safe::lua_pushrclosure(state, userdata_destructor::<T>, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__gc")?;
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
rawset_field(state, -2, "__gc")?;
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
if let Some(f) = customize_fn {
|
||||
f(state)?;
|
||||
}
|
||||
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void)?;
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = &METATABLE_CACHE[&type_id] as *const u8;
|
||||
protect_lua!(state, 1, 0, |state| {
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
|
||||
})?;
|
||||
|
||||
Ok(ref_addr)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = {
|
||||
let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||
mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8
|
||||
};
|
||||
let ref_addr =
|
||||
mlua_expect!(METATABLE_CACHE.get(&type_id), "gc metatable does not exist") as *const u8;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
|
||||
}
|
||||
|
||||
// Initialize the error, panic, and destructed userdata metatables.
|
||||
// Returns address of WrappedError and WrappedPanic metatables in Lua registry.
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const u8, *const u8)> {
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 7)?;
|
||||
|
||||
// Create error and panic metatables
|
||||
@@ -464,80 +724,92 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||
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);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1).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);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
(*err_buf).clear();
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
Ok(err_buf)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
(*err_buf).clear();
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
// Find first two sources that caused the error
|
||||
let mut source1 = error.source();
|
||||
let mut source0 = source1.and_then(|s| s.source());
|
||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||
source1 = source0;
|
||||
source0 = Some(source);
|
||||
}
|
||||
match (source1, source0) {
|
||||
(_, Some(error0))
|
||||
if error0.to_string().contains("\nstack traceback:\n") =>
|
||||
{
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
}
|
||||
(Some(error1), Some(error0)) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
let s = error1.to_string();
|
||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||
let _ =
|
||||
write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||
}
|
||||
}
|
||||
(Some(error1), None) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
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);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
(*err_buf).clear();
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
if let Some(msg) = p.downcast_ref::<&str>() {
|
||||
if let Some(msg) = panic.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else if let Some(msg) = p.downcast_ref::<String>() {
|
||||
} else if let Some(msg) = panic.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else {
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
Ok(err_buf)
|
||||
} else {
|
||||
Err(Error::PreviouslyResumedPanic)
|
||||
}
|
||||
} else {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
|
||||
_ => {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
}?;
|
||||
|
||||
ffi::safe::lua_pushstring(state, &*err_buf)?;
|
||||
push_string(state, &*err_buf)?;
|
||||
(*err_buf).clear();
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
let wrapped_error_key = init_gc_metatable_for::<WrappedError>(
|
||||
init_gc_metatable::<WrappedFailure>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::safe::lua_pushrclosure(state, error_tostring, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__tostring")
|
||||
}),
|
||||
)?;
|
||||
|
||||
let wrapped_panic_key = init_gc_metatable_for::<WrappedPanic>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::safe::lua_pushrclosure(state, error_tostring, 0)?;
|
||||
ffi::safe::lua_rawsetfield(state, -2, "__tostring")
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
rawset_field(state, -2, "__tostring")
|
||||
}),
|
||||
)?;
|
||||
|
||||
// Create destructed userdata metatable
|
||||
|
||||
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 2)?;
|
||||
let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
|
||||
ptr::write(error_ud, WrappedError(Error::CallbackDestructed));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(-1) // to trigger lua_error
|
||||
})
|
||||
callback_error(state, |_| Err(Error::CallbackDestructed))
|
||||
}
|
||||
|
||||
ffi::safe::lua_createtable(state, 0, 26)?;
|
||||
ffi::safe::lua_pushrclosure(state, destructed_error, 0)?;
|
||||
push_table(state, 0, 26)?;
|
||||
ffi::lua_pushcfunction(state, destructed_error);
|
||||
for &method in &[
|
||||
"__add",
|
||||
"__sub",
|
||||
@@ -577,28 +849,34 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
|
||||
"__close",
|
||||
] {
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::safe::lua_rawsetfield(state, -3, method)?;
|
||||
rawset_field(state, -3, method)?;
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
|
||||
})?;
|
||||
|
||||
// Create error print buffer
|
||||
init_gc_metatable_for::<String>(state, None)?;
|
||||
init_gc_metatable::<String>(state, None)?;
|
||||
push_gc_userdata(state, String::new())?;
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)?;
|
||||
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);
|
||||
})?;
|
||||
|
||||
Ok((wrapped_error_key, wrapped_panic_key))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedError(pub Error);
|
||||
pub(crate) struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||
pub(crate) enum WrappedFailure {
|
||||
Error(Error),
|
||||
Panic(Option<Box<dyn Any + Send + 'static>>),
|
||||
}
|
||||
|
||||
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||
// panicking.
|
||||
unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
|
||||
pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
|
||||
match ffi::lua_type(state, index) {
|
||||
ffi::LUA_TNONE => "<none>".to_string(),
|
||||
ffi::LUA_TNIL => "<nil>".to_string(),
|
||||
|
||||
@@ -157,18 +157,21 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Default for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn default() -> MultiValue<'lua> {
|
||||
MultiValue::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
||||
MultiValue::from_vec(Vec::from_iter(iter))
|
||||
}
|
||||
@@ -178,6 +181,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
|
||||
type Item = Value<'lua>;
|
||||
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter().rev()
|
||||
}
|
||||
@@ -187,43 +191,52 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
||||
type Item = &'a Value<'lua>;
|
||||
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
(&self.0).iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
#[inline]
|
||||
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
v.reverse();
|
||||
MultiValue(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value<'lua>> {
|
||||
let mut v = self.0;
|
||||
v.reverse();
|
||||
v
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reserve(&mut self, size: usize) {
|
||||
self.0.reserve(size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.0.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||
self.0.iter().rev()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use bstr::{BStr, BString};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn byte_string_round_trip() -> Result<()> {
|
||||
fn test_byte_string_round_trip() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
|
||||
@@ -4,7 +4,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||
10 | Ok(test.0)
|
||||
| ---- `test` is borrowed here
|
||||
| ------ `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> $DIR/function_borrow.rs:9:13
|
||||
|
||||
@@ -14,3 +14,22 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= 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]`
|
||||
|
||||
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
|
||||
--> $DIR/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
|
||||
|
|
||||
::: $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`
|
||||
|
|
||||
= 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>>`
|
||||
= 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]`
|
||||
|
||||
@@ -16,3 +16,24 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= 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]`
|
||||
|
||||
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
|
||||
--> $DIR/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
|
||||
|
|
||||
::: $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`
|
||||
|
|
||||
= 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>>`
|
||||
= 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]`
|
||||
|
||||
@@ -7,7 +7,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test`
|
||||
15 | test.field = 42;
|
||||
| ---- `test` is borrowed here
|
||||
| ---------- `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> $DIR/scope_invariance.rs:13:13
|
||||
|
||||
@@ -2,7 +2,7 @@ use mlua::{Lua, UserData};
|
||||
|
||||
fn main() {
|
||||
struct MyUserData<'a>(&'a mut i32);
|
||||
impl<'a> UserData for MyUserData<'a> {};
|
||||
impl<'a> UserData for MyUserData<'a> {}
|
||||
|
||||
let mut i = 1;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use mlua::{Lua, UserData};
|
||||
fn main() {
|
||||
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
||||
struct MyUserData<'a>(&'a i32);
|
||||
impl<'a> UserData for MyUserData<'a> {};
|
||||
impl<'a> UserData for MyUserData<'a> {}
|
||||
|
||||
let igood = 1;
|
||||
|
||||
|
||||
+21
-10
@@ -1,6 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::CString;
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{Lua, Result};
|
||||
@@ -12,7 +12,7 @@ fn test_conv_vec() -> Result<()> {
|
||||
let v = vec![1, 2, 3];
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Vec<i32> = lua.globals().get("v")?;
|
||||
assert!(v == v2);
|
||||
assert_eq!(v, v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -24,7 +24,7 @@ fn test_conv_hashmap() -> Result<()> {
|
||||
let map = hashmap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: HashMap<String, String> = lua.globals().get("map")?;
|
||||
assert!(map == map2);
|
||||
assert_eq!(map, map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -36,7 +36,10 @@ fn test_conv_hashset() -> Result<()> {
|
||||
let set = hashset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: HashSet<String> = lua.globals().get("set")?;
|
||||
assert!(set == set2);
|
||||
assert_eq!(set, set2);
|
||||
|
||||
let set3 = lua.load(r#"{"a", "b", "c"}"#).eval::<HashSet<String>>()?;
|
||||
assert_eq!(set3, hashset! { "a".into(), "b".into(), "c".into() });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -48,7 +51,7 @@ fn test_conv_btreemap() -> Result<()> {
|
||||
let map = btreemap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: BTreeMap<String, String> = lua.globals().get("map")?;
|
||||
assert!(map == map2);
|
||||
assert_eq!(map, map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -60,7 +63,10 @@ fn test_conv_btreeset() -> Result<()> {
|
||||
let set = btreeset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: BTreeSet<String> = lua.globals().get("set")?;
|
||||
assert!(set == set2);
|
||||
assert_eq!(set, set2);
|
||||
|
||||
let set3 = lua.load(r#"{"a", "b", "c"}"#).eval::<BTreeSet<String>>()?;
|
||||
assert_eq!(set3, btreeset! { "a".into(), "b".into(), "c".into() });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -72,7 +78,12 @@ fn test_conv_cstring() -> Result<()> {
|
||||
let s = CString::new(b"hello".to_vec()).unwrap();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: CString = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
assert_eq!(s, s2);
|
||||
|
||||
let cs = CStr::from_bytes_with_nul(b"hello\0").unwrap();
|
||||
lua.globals().set("cs", cs)?;
|
||||
let cs2: CString = lua.globals().get("cs")?;
|
||||
assert_eq!(cs, cs2.as_c_str());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -84,7 +95,7 @@ fn test_conv_cow() -> Result<()> {
|
||||
let s = Cow::from("hello");
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: String = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
assert_eq!(s, s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -96,7 +107,7 @@ fn test_conv_boxed_str() -> Result<()> {
|
||||
let s = String::from("hello").into_boxed_str();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: Box<str> = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
assert_eq!(s, s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -108,7 +119,7 @@ fn test_conv_boxed_slice() -> Result<()> {
|
||||
let v = vec![1, 2, 3].into_boxed_slice();
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Box<[i32]> = lua.globals().get("v")?;
|
||||
assert!(v == v2);
|
||||
assert_eq!(v, v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -76,6 +76,23 @@ fn test_rust_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_c_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
unsafe extern "C" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
lua.globals().set("c_function", true).unwrap();
|
||||
0
|
||||
}
|
||||
|
||||
let func = unsafe { lua.create_c_function(c_function)? };
|
||||
func.call(())?;
|
||||
assert_eq!(lua.globals().get::<_, bool>("c_function")?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dump() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
|
||||
+54
-70
@@ -3,24 +3,32 @@ use std::ops::Deref;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use mlua::{Error, HookTriggers, Lua, Result, Value};
|
||||
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn line_counts() -> Result<()> {
|
||||
fn test_hook_triggers_bitor() {
|
||||
let trigger = HookTriggers::on_calls()
|
||||
| HookTriggers::on_returns()
|
||||
| HookTriggers::every_line()
|
||||
| HookTriggers::every_nth_instruction(5);
|
||||
|
||||
assert!(trigger.on_calls);
|
||||
assert!(trigger.on_returns);
|
||||
assert!(trigger.every_line);
|
||||
assert_eq!(trigger.every_nth_instruction, Some(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_counts() -> Result<()> {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, debug| {
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
})?;
|
||||
lua.load(
|
||||
r#"
|
||||
local x = 2 + 3
|
||||
@@ -43,25 +51,20 @@ fn line_counts() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_calls() -> Result<()> {
|
||||
fn test_function_calls() -> Result<()> {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, debug| {
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Call);
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -95,19 +98,14 @@ fn function_calls() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_within_hook() -> Result<()> {
|
||||
fn test_error_within_hook() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
|
||||
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
let err = lua
|
||||
.load("x = 1")
|
||||
@@ -126,7 +124,7 @@ fn error_within_hook() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_execution_instructions() -> Result<()> {
|
||||
fn test_limit_execution_instructions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut max_instructions = 10000;
|
||||
|
||||
@@ -135,11 +133,9 @@ fn limit_execution_instructions() -> Result<()> {
|
||||
lua.load("jit.off()").exec()?;
|
||||
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(30),
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, _debug| {
|
||||
HookTriggers::every_nth_instruction(30),
|
||||
move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Count);
|
||||
max_instructions -= 30;
|
||||
if max_instructions < 0 {
|
||||
Err(Error::RuntimeError("time's up".to_string()))
|
||||
@@ -165,20 +161,14 @@ fn limit_execution_instructions() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hook_removal() -> Result<()> {
|
||||
fn test_hook_removal() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(1),
|
||||
..Default::default()
|
||||
},
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
assert!(lua.load("local x = 1").exec().is_err());
|
||||
lua.remove_hook();
|
||||
@@ -188,7 +178,7 @@ fn hook_removal() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hook_swap_within_hook() -> Result<()> {
|
||||
fn test_hook_swap_within_hook() -> Result<()> {
|
||||
thread_local! {
|
||||
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
|
||||
}
|
||||
@@ -198,19 +188,14 @@ fn hook_swap_within_hook() -> Result<()> {
|
||||
});
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |lua, _debug| {
|
||||
tl.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
|
||||
lua.globals().set("ok", 1i64)?;
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
HookTriggers::every_line(),
|
||||
move |lua, _debug| {
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -228,8 +213,7 @@ fn hook_swap_within_hook() -> Result<()> {
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#![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(())
|
||||
}
|
||||
@@ -38,6 +38,9 @@ fn test_gc_control() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
assert_eq!(lua.gc_gen(0, 0), mlua::GCMode::Incremental);
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
assert!(lua.gc_is_running());
|
||||
@@ -59,6 +62,9 @@ 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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,32 @@ fn test_module() -> Result<()> {
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_multi() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("rust_module")
|
||||
local mod2 = require("rust_module.second")
|
||||
assert(mod.check_userdata(mod2.userdata) == 123)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_error() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(require, "rust_module.error")
|
||||
assert(not ok)
|
||||
assert(string.find(tostring(err), "custom module error"))
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
|
||||
+9
-9
@@ -8,7 +8,7 @@ use mlua::{
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn scope_func() -> Result<()> {
|
||||
fn test_scope_func() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let rc = Rc::new(Cell::new(0));
|
||||
@@ -38,7 +38,7 @@ fn scope_func() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_capture() -> Result<()> {
|
||||
fn test_scope_capture() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mut i = 0;
|
||||
@@ -56,7 +56,7 @@ fn scope_capture() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_outer_lua_access() -> Result<()> {
|
||||
fn test_scope_outer_lua_access() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_table()?;
|
||||
@@ -71,7 +71,7 @@ fn scope_outer_lua_access() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_fields() -> Result<()> {
|
||||
fn test_scope_userdata_fields() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -106,7 +106,7 @@ fn scope_userdata_fields() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_methods() -> Result<()> {
|
||||
fn test_scope_userdata_methods() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -147,7 +147,7 @@ fn scope_userdata_methods() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_functions() -> Result<()> {
|
||||
fn test_scope_userdata_functions() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a i64);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -189,7 +189,7 @@ fn scope_userdata_functions() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_mismatch() -> Result<()> {
|
||||
fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -241,7 +241,7 @@ fn scope_userdata_mismatch() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_drop() -> Result<()> {
|
||||
fn test_scope_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Rc<()>);
|
||||
@@ -299,7 +299,7 @@ fn scope_userdata_drop() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData<'a>(&'a Cell<i64>, Arc<()>);
|
||||
|
||||
+87
-2
@@ -2,7 +2,10 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData, Value};
|
||||
use mlua::{
|
||||
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||
Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
@@ -302,6 +305,36 @@ fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_nested_tables() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
local table_a = {a = "a"}
|
||||
local table_b = {"b"}
|
||||
return {
|
||||
a = table_a,
|
||||
b = {table_b, table_b},
|
||||
ab = {a = table_a, b = table_b}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
let got = lua.from_value::<serde_json::Value>(value)?;
|
||||
assert_eq!(
|
||||
got,
|
||||
serde_json::json!({
|
||||
"a": {"a": "a"},
|
||||
"b": [["b"], ["b"]],
|
||||
"ab": {"a": {"a": "a"}, "b": ["b"]},
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
@@ -310,7 +343,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
struct Test {
|
||||
int: u32,
|
||||
seq: Vec<String>,
|
||||
map: std::collections::HashMap<i32, i32>,
|
||||
map: HashMap<i32, i32>,
|
||||
empty: Vec<()>,
|
||||
tuple: (u8, u8, u8),
|
||||
}
|
||||
@@ -413,3 +446,55 @@ fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Deny unsupported types by default
|
||||
let value = Value::Function(lua.create_function(|_, ()| Ok(()))?);
|
||||
match lua.from_value::<Option<String>>(value) {
|
||||
Ok(v) => panic!("expected deserialization error, got {:?}", v),
|
||||
Err(Error::DeserializeError(err)) => {
|
||||
assert!(err.contains("unsupported value type"))
|
||||
}
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Allow unsupported types
|
||||
let value = Value::Function(lua.create_function(|_, ()| Ok(()))?);
|
||||
let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
assert_eq!(lua.from_value_with::<()>(value, options)?, ());
|
||||
|
||||
// Allow unsupported types (in a table seq)
|
||||
let value = lua.load(r#"{"a", "b", function() end, "c"}"#).eval()?;
|
||||
let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
assert_eq!(
|
||||
lua.from_value_with::<Vec<String>>(value, options)?,
|
||||
vec!["a".to_string(), "b".to_string(), "c".to_string()]
|
||||
);
|
||||
|
||||
// Deny recursive tables by default
|
||||
let value = lua.load(r#"local t = {}; t.t = t; return t"#).eval()?;
|
||||
match lua.from_value::<HashMap<String, Option<String>>>(value) {
|
||||
Ok(v) => panic!("expected deserialization error, got {:?}", v),
|
||||
Err(Error::DeserializeError(err)) => {
|
||||
assert!(err.contains("recursive table detected"))
|
||||
}
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Serialize Lua globals table
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Globals {
|
||||
hello: String,
|
||||
}
|
||||
let options = DeserializeOptions::new()
|
||||
.deny_unsupported_types(false)
|
||||
.deny_recursive_tables(false);
|
||||
lua.load(r#"hello = "world""#).exec()?;
|
||||
let globals: Globals = lua.from_value_with(Value::Table(lua.globals()), options)?;
|
||||
assert_eq!(globals.hello, "world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+7
-3
@@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||
use mlua::{Lua, Result, String};
|
||||
|
||||
#[test]
|
||||
fn compare() {
|
||||
fn test_string_compare() {
|
||||
fn with_str<F: FnOnce(String)>(s: &str, f: F) {
|
||||
f(Lua::new().create_string(s).unwrap());
|
||||
}
|
||||
@@ -21,7 +21,7 @@ fn compare() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_views() -> Result<()> {
|
||||
fn test_string_views() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
@@ -39,6 +39,10 @@ fn string_views() -> Result<()> {
|
||||
let empty: String = globals.get("empty")?;
|
||||
|
||||
assert_eq!(ok.to_str()?, "null bytes are valid utf-8, wh\0 knew?");
|
||||
assert_eq!(
|
||||
ok.to_string_lossy(),
|
||||
"null bytes are valid utf-8, wh\0 knew?"
|
||||
);
|
||||
assert_eq!(
|
||||
ok.as_bytes(),
|
||||
&b"null bytes are valid utf-8, wh\0 knew?"[..]
|
||||
@@ -55,7 +59,7 @@ fn string_views() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_string() -> Result<()> {
|
||||
fn test_raw_string() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let rs = lua.create_string(&[0, 1, 2, 3, 0, 1, 2, 3])?;
|
||||
|
||||
+82
-19
@@ -1,6 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
@@ -82,6 +84,7 @@ fn test_safety() -> Result<()> {
|
||||
#[test]
|
||||
fn test_load() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func = lua.load("return 1+2").into_function()?;
|
||||
let result: i32 = func.call(())?;
|
||||
assert_eq!(result, 3);
|
||||
@@ -394,7 +397,7 @@ fn test_panic() -> Result<()> {
|
||||
Ok(lua)
|
||||
}
|
||||
|
||||
// Test triggerting Lua error passing Rust panic (must be resumed)
|
||||
// Test triggering Lua error with sending Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
|
||||
@@ -450,7 +453,7 @@ fn test_panic() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Test representing rust panic as a string
|
||||
// Test representing Rust panic as a string
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
lua.load(
|
||||
@@ -485,7 +488,7 @@ fn test_panic() -> Result<()> {
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
// Test enabling `catch_rust_panics` option / xpcall correctness
|
||||
// Test disabling `catch_rust_panics` option / xpcall correctness
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?;
|
||||
lua.load(
|
||||
@@ -611,7 +614,7 @@ fn test_pcall_xpcall() -> Result<()> {
|
||||
assert!(lua.load("xpcall()").exec().is_err());
|
||||
assert!(lua.load("xpcall(function() end)").exec().is_err());
|
||||
|
||||
// Lua 5.3/5.2 / LuaJIT compatible version of xpcall
|
||||
// Lua >= 5.2 compatible version of xpcall for 5.1
|
||||
#[cfg(feature = "lua51")]
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -797,6 +800,23 @@ fn test_drop_registry_value() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lua_registry_hash() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let r1 = Arc::new(lua.create_registry_value("value1")?);
|
||||
let r2 = Arc::new(lua.create_registry_value("value2")?);
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(r1.clone(), "value1");
|
||||
map.insert(r2.clone(), "value2");
|
||||
|
||||
assert_eq!(map[&r1], "value1");
|
||||
assert_eq!(map[&r2], "value2");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lua_registry_ownership() -> Result<()> {
|
||||
let lua1 = Lua::new();
|
||||
@@ -828,7 +848,26 @@ fn test_mismatched_registry_key() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_returns() -> Result<()> {
|
||||
fn test_recursion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(move |lua, i: i32| {
|
||||
if i < 64 {
|
||||
lua.globals()
|
||||
.get::<_, Function>("f")?
|
||||
.call::<_, ()>(i + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
f.call::<_, ()>(1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_too_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
||||
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
||||
@@ -836,7 +875,7 @@ fn too_many_returns() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_arguments() -> Result<()> {
|
||||
fn test_too_many_arguments() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.load("function test(...) end").exec()?;
|
||||
let args = Variadic::from_iter(1..1000000);
|
||||
@@ -850,23 +889,20 @@ fn too_many_arguments() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
fn too_many_recursions() -> Result<()> {
|
||||
fn test_too_many_recursions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
||||
lua.globals().set("f", f)?;
|
||||
|
||||
assert!(lua
|
||||
.globals()
|
||||
.get::<_, Function>("f")?
|
||||
.call::<_, ()>(())
|
||||
.is_err());
|
||||
lua.globals().set("f", f.clone())?;
|
||||
assert!(f.call::<_, ()>(()).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_binds() -> Result<()> {
|
||||
fn test_too_many_binds() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
@@ -905,7 +941,7 @@ fn test_ref_stack_exhaustion() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_args() -> Result<()> {
|
||||
fn test_large_args() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
@@ -940,7 +976,7 @@ fn large_args() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_args_ref() -> Result<()> {
|
||||
fn test_large_args_ref() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(|_, args: Variadic<String>| {
|
||||
@@ -956,7 +992,7 @@ fn large_args_ref() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_env() -> Result<()> {
|
||||
fn test_chunk_env() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let assert: Function = lua.globals().get("assert")?;
|
||||
@@ -998,7 +1034,7 @@ fn chunk_env() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_thread() -> Result<()> {
|
||||
fn test_context_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
@@ -1021,7 +1057,7 @@ fn context_thread() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
fn context_thread_51() -> Result<()> {
|
||||
fn test_context_thread_51() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let thread = lua.create_thread(
|
||||
@@ -1051,3 +1087,30 @@ fn test_jit_version() -> Result<()> {
|
||||
.contains("LuaJIT"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_from_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Arc::new(AtomicU32::new(0));
|
||||
let i2 = i.clone();
|
||||
let func = lua.create_function(move |lua, modname: String| {
|
||||
i2.fetch_add(1, Ordering::Relaxed);
|
||||
let t = lua.create_table()?;
|
||||
t.set("__name", modname)?;
|
||||
Ok(t)
|
||||
})?;
|
||||
|
||||
let t: Table = lua.load_from_function("my_module", func.clone())?;
|
||||
assert_eq!(t.get::<_, String>("__name")?, "my_module");
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let _: Value = lua.load_from_function("my_module", func)?;
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let func_nil = lua.create_function(move |_, _: String| Ok(Value::Nil))?;
|
||||
let v: Value = lua.load_from_function("my_module2", func_nil)?;
|
||||
assert_eq!(v, Value::Boolean(true));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+45
-2
@@ -94,7 +94,50 @@ fn test_thread() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coroutine_from_closure() -> Result<()> {
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
fn test_thread_reset() -> Result<()> {
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use std::sync::Arc;
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Arc<()>);
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let arc = Arc::new(());
|
||||
|
||||
let func: Function = lua.load(r#"function(ud) coroutine.yield(ud) end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()))?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
thread.resume::<_, ()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Unresumable);
|
||||
thread.reset(func.clone())?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
}
|
||||
|
||||
// Check for errors (Lua 5.4 only)
|
||||
#[cfg(feature = "lua54")]
|
||||
{
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coroutine_from_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let thrd_main = lua.create_function(|_, ()| Ok(()))?;
|
||||
@@ -118,7 +161,7 @@ fn coroutine_from_closure() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coroutine_panic() {
|
||||
fn test_coroutine_panic() {
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
// check that coroutines propagate panics correctly
|
||||
let lua = Lua::new();
|
||||
|
||||
+79
-1
@@ -1,4 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
@@ -359,6 +362,12 @@ fn test_fields() -> Result<()> {
|
||||
index.set("f", 321)?;
|
||||
Ok(index)
|
||||
});
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
lua.create_function(|lua, (_, field, val): (AnyUserData, String, Value)| {
|
||||
lua.globals().set(field, val)?;
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +385,9 @@ fn test_fields() -> Result<()> {
|
||||
assert(ud.uval == "hello")
|
||||
|
||||
assert(ud.f == 321)
|
||||
|
||||
ud.unknown = 789
|
||||
assert(unknown == 789)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -451,3 +463,69 @@ fn test_metatable() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_wrapped() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud1 = Rc::new(RefCell::new(MyUserData(1)));
|
||||
globals.set("rc_refcell_ud", ud1.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud1.borrow().0, 2);
|
||||
globals.set("rc_refcell_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Rc::strong_count(&ud1), 1);
|
||||
}
|
||||
|
||||
let ud2 = Arc::new(Mutex::new(MyUserData(2)));
|
||||
globals.set("arc_mutex_ud", ud2.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud2.lock().unwrap().0, 3);
|
||||
|
||||
let ud3 = Arc::new(RwLock::new(MyUserData(3)));
|
||||
globals.set("arc_rwlock_ud", ud3.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud3.read().unwrap().0, 4);
|
||||
|
||||
// Test drop
|
||||
globals.set("arc_mutex_ud", Nil)?;
|
||||
globals.set("arc_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud2), 1);
|
||||
assert_eq!(Arc::strong_count(&ud3), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user