mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fe898c0dd | |||
| 821f1125b6 | |||
| 6c0096d8ac | |||
| abb95c3c56 | |||
| 64faebf407 | |||
| a944f4ad6f | |||
| bae424672a | |||
| 6f9eb82649 | |||
| e8de2a458a | |||
| 973b5c3bf5 | |||
| b610a79d66 | |||
| fe39ae09bf | |||
| 01714d2510 | |||
| 0bad4a0ff9 | |||
| 35b7504076 | |||
| c9b8eb5418 | |||
| ef7d123f80 | |||
| 20cba5de5b | |||
| 3e03f4201c | |||
| 5199b02346 | |||
| 5293b8d6d2 | |||
| 7541b6f3f3 | |||
| 205510a540 | |||
| 2250421438 |
@@ -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
|
||||
|
||||
+12
-12
@@ -26,8 +26,8 @@ jobs:
|
||||
override: true
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
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
|
||||
@@ -73,7 +73,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:
|
||||
@@ -96,7 +96,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:
|
||||
@@ -124,14 +124,14 @@ jobs:
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
@@ -157,8 +157,8 @@ jobs:
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }} vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }} vendored")
|
||||
(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 +210,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,19 @@
|
||||
## 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
|
||||
|
||||
+4
-3
@@ -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.0-beta.3" # 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,9 +38,10 @@ 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-beta.2", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.7" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
|
||||
@@ -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.0-beta", 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.0-beta", features = ["lua53", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -233,7 +234,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.
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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::{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)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result};
|
||||
use mlua::{ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
@@ -10,11 +10,8 @@ async fn main() -> Result<()> {
|
||||
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)?;
|
||||
|
||||
@@ -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::{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)
|
||||
});
|
||||
|
||||
|
||||
@@ -8,10 +8,27 @@ 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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0-beta.2"
|
||||
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()
|
||||
}
|
||||
}
|
||||
+86
-3
@@ -1,16 +1,20 @@
|
||||
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};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error, quote::quote,
|
||||
};
|
||||
|
||||
#[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);
|
||||
|
||||
if args.len() > 0 {
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
@@ -35,3 +39,82 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
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) -> Option<Result<Value<'lua>>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return Some(make_env(lua));
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
+5
-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,10 +174,7 @@ pub enum Error {
|
||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
#[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.
|
||||
@@ -246,7 +245,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 +274,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 {
|
||||
|
||||
+17
-1
@@ -156,8 +156,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 +221,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;
|
||||
|
||||
+9
-4
@@ -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;
|
||||
|
||||
@@ -253,16 +256,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(
|
||||
|
||||
+37
-31
@@ -31,21 +31,37 @@ 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 void wrapped_error_traceback(lua_State *L, int error_idx,
|
||||
int traceback_idx);
|
||||
|
||||
extern int mlua_hook_proc(lua_State *L, lua_Debug *ar);
|
||||
|
||||
#define max(a, b) (a > b ? a : b)
|
||||
|
||||
// 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;
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
size_t len;
|
||||
} StringArg;
|
||||
|
||||
static void handle_wrapped_error(lua_State *L) {
|
||||
if (lua_checkstack(L, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(L, L, NULL, 0);
|
||||
// Convert to CallbackError and attach traceback
|
||||
wrapped_error_traceback(L, -2, -1);
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
// Convert to CallbackError with error message as a traceback
|
||||
wrapped_error_traceback(L, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Rust callback expected to return positive number of output values or
|
||||
// -1 in case of error, -2 in case of panic.
|
||||
static int lua_call_rust(lua_State *L) {
|
||||
int nargs = lua_gettop(L);
|
||||
|
||||
@@ -67,7 +83,10 @@ static int lua_call_rust(lua_State *L) {
|
||||
lua_CFunction rust_callback = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
int ret = rust_callback(L);
|
||||
if (ret == -1) {
|
||||
if (ret < 0) {
|
||||
if (ret == -1 /* WrappedError */) {
|
||||
handle_wrapped_error(L);
|
||||
}
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
@@ -79,7 +98,10 @@ void lua_call_mlua_hook_proc(lua_State *L, lua_Debug *ar) {
|
||||
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) {
|
||||
if (ret < 0) {
|
||||
if (ret == -1 /* WrappedError */) {
|
||||
handle_wrapped_error(L);
|
||||
}
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
@@ -393,42 +415,26 @@ int is_wrapped_struct(lua_State *state, int index, const void *key) {
|
||||
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
|
||||
// Takes an error at the top of the stack and converts Lua errors into a string
|
||||
// with attached traceback. If the error is a WrappedError or 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.
|
||||
// and shadowing previous rust errors.
|
||||
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);
|
||||
if (MLUA_WRAPPED_PANIC_KEY == NULL ||
|
||||
is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY) ||
|
||||
is_wrapped_struct(state, -1, MLUA_WRAPPED_ERROR_KEY)) {
|
||||
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);
|
||||
const char *s = luaL_tolstring(state, -1, NULL);
|
||||
if (lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(state, state, s, 1);
|
||||
lua_remove(state, -2);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -90,7 +90,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"`
|
||||
///
|
||||
|
||||
+2
-2
@@ -11,7 +11,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
|
||||
@@ -174,7 +174,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)]
|
||||
|
||||
+60
-8
@@ -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.0-beta.3")]
|
||||
// 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,13 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::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::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
@@ -111,7 +112,9 @@ 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")]
|
||||
@@ -126,11 +129,60 @@ pub mod prelude;
|
||||
#[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 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).
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals.
|
||||
/// (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;
|
||||
|
||||
+198
-33
@@ -6,7 +6,7 @@ use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::sync::{Arc, Mutex, MutexGuard, Weak};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak};
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -32,6 +32,9 @@ use crate::util::{
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
@@ -97,7 +100,7 @@ pub enum GCMode {
|
||||
Generational,
|
||||
}
|
||||
|
||||
/// Controls Lua interpreter behaviour such as Rust panics handling.
|
||||
/// Controls Lua interpreter behavior such as Rust panics handling.
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct LuaOptions {
|
||||
@@ -107,7 +110,7 @@ pub struct LuaOptions {
|
||||
/// Also in Lua 5.1 adds ability to provide arguments to [`xpcall`] similar to Lua >= 5.2.
|
||||
///
|
||||
/// If enabled, keeps [`pcall`]/[`xpcall`] unmodified.
|
||||
/// Panics are still automatically resumed if returned back to the Rust side.
|
||||
/// Panics are still automatically resumed if returned to the Rust side.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
@@ -125,7 +128,7 @@ impl Default for LuaOptions {
|
||||
}
|
||||
|
||||
impl LuaOptions {
|
||||
/// Retruns a new instance of `LuaOptions` with default parameters.
|
||||
/// Returns a new instance of `LuaOptions` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
@@ -206,7 +209,7 @@ impl Lua {
|
||||
|
||||
/// Creates a new Lua state and loads the specified safe subset of the standard libraries.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
|
||||
@@ -243,7 +246,7 @@ impl Lua {
|
||||
|
||||
/// Creates a new Lua state and loads the specified subset of the standard libraries.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state will not have safety guarantees and allow to load C modules.
|
||||
@@ -300,7 +303,7 @@ impl Lua {
|
||||
if !new_ptr.is_null() {
|
||||
mem_info.used_memory += mem_diff;
|
||||
} else if !ptr.is_null() && nsize < osize {
|
||||
// Should not happend
|
||||
// Should not happen
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
}
|
||||
|
||||
@@ -369,6 +372,10 @@ impl Lua {
|
||||
let main_state = maybe_main_state.unwrap_or(state);
|
||||
let main_state_top = ffi::lua_gettop(main_state);
|
||||
|
||||
if let Some(lua) = Lua::make_from_ptr(state) {
|
||||
return lua;
|
||||
}
|
||||
|
||||
let ref_thread = mlua_expect!(
|
||||
(|state| {
|
||||
// Before initializing the error registry, we must set Error/Panic size.
|
||||
@@ -437,8 +444,8 @@ impl Lua {
|
||||
);
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
mlua_expect!(
|
||||
ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key,),
|
||||
"Error while storing extra data"
|
||||
ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key),
|
||||
"Error while storing extra data",
|
||||
);
|
||||
|
||||
mlua_debug_assert!(
|
||||
@@ -459,7 +466,7 @@ impl Lua {
|
||||
|
||||
/// Loads the specified subset of the standard libraries into an existing Lua state.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// [`StdLib`]: struct.StdLib.html
|
||||
pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> {
|
||||
@@ -769,14 +776,14 @@ impl Lua {
|
||||
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
S: AsChunk<'lua> + ?Sized,
|
||||
{
|
||||
Chunk {
|
||||
lua: self,
|
||||
source: source.as_ref(),
|
||||
name: None,
|
||||
env: None,
|
||||
mode: None,
|
||||
source: source.source(),
|
||||
name: source.name(),
|
||||
env: source.env(self),
|
||||
mode: source.mode(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1603,6 +1610,11 @@ impl Lua {
|
||||
self.push_value(f(self)?)?;
|
||||
ffi::safe::lua_rawsetfield(self.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
// Add special `__mlua_type_id` field
|
||||
let type_id_ptr =
|
||||
ffi::safe::lua_newuserdata(self.state, mem::size_of::<TypeId>())? as *mut TypeId;
|
||||
ptr::write(type_id_ptr, type_id);
|
||||
ffi::safe::lua_rawsetfield(self.state, -2, "__mlua_type_id")?;
|
||||
let metatable_index = ffi::lua_absindex(self.state, -1);
|
||||
|
||||
let mut extra_tables_count = 0;
|
||||
@@ -1686,7 +1698,7 @@ impl Lua {
|
||||
// Pushes a LuaRef value onto the stack, checking that it's a registered
|
||||
// and not destructed UserData.
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef, with_mt: bool) -> Result<()> {
|
||||
self.push_ref(lref);
|
||||
if ffi::lua_getmetatable(self.state, -1) == 0 {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
@@ -1695,7 +1707,9 @@ impl Lua {
|
||||
let ptr = ffi::lua_topointer(self.state, -1);
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
if extra.registered_userdata_mt.contains(&(ptr as isize)) {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
if !with_mt {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// Maybe userdata was destructed?
|
||||
@@ -1840,6 +1854,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
lua.state = state;
|
||||
|
||||
// Try to get an outer poll waker
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
@@ -1971,12 +1986,14 @@ impl Lua {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Self {
|
||||
pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Option<Self> {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
return None;
|
||||
}
|
||||
let extra = mlua_expect!(
|
||||
(*get_gc_userdata::<Weak<Mutex<ExtraData>>>(state, -1)).upgrade(),
|
||||
"extra is destroyed"
|
||||
@@ -1985,14 +2002,14 @@ impl Lua {
|
||||
|
||||
let safe = mlua_expect!(extra.lock(), "extra is poisoned").safe;
|
||||
|
||||
Lua {
|
||||
Some(Lua {
|
||||
state,
|
||||
main_state: get_main_state(state),
|
||||
extra,
|
||||
ephemeral: true,
|
||||
safe,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn hook_callback(&self) -> Option<HookCallback> {
|
||||
@@ -2009,7 +2026,7 @@ pub struct Chunk<'lua, 'a> {
|
||||
lua: &'lua Lua,
|
||||
source: &'a [u8],
|
||||
name: Option<CString>,
|
||||
env: Option<Value<'lua>>,
|
||||
env: Option<Result<Value<'lua>>>,
|
||||
mode: Option<ChunkMode>,
|
||||
}
|
||||
|
||||
@@ -2020,6 +2037,32 @@ pub enum ChunkMode {
|
||||
Binary,
|
||||
}
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.3/manual.html#3.3.2
|
||||
/// [`Chunk`]: struct.Chunk.html
|
||||
pub trait AsChunk<'lua> {
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(&self) -> &[u8];
|
||||
|
||||
/// Returns optional chunk name
|
||||
fn name(&self) -> Option<CString> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.3/manual.html#2.2
|
||||
fn env(&self, _lua: &'lua Lua) -> Option<Result<Value<'lua>>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns optional chunk mode (text or binary)
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name<S: AsRef<[u8]> + ?Sized>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
|
||||
@@ -2045,7 +2088,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Chunk<'lua, 'a>> {
|
||||
self.env = Some(env.to_lua(self.lua)?);
|
||||
// Prefer to propagate errors here and wrap to `Ok`
|
||||
self.env = Some(Ok(env.to_lua(self.lua)?));
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
@@ -2099,7 +2143,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
} else if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
self.env()?,
|
||||
self.mode,
|
||||
) {
|
||||
function.call(())
|
||||
@@ -2127,7 +2171,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
} else if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
match self.env() {
|
||||
Ok(env) => env,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
},
|
||||
self.mode,
|
||||
) {
|
||||
function.call_async(())
|
||||
@@ -2136,14 +2183,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the chunk function and call it with the given arguemnts.
|
||||
/// Load the chunk function and call it with the given arguments.
|
||||
///
|
||||
/// This is equivalent to `into_function` and calling the resulting function.
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
/// Load the chunk function and asynchronously call it with the given arguemnts.
|
||||
/// Load the chunk function and asynchronously call it with the given arguments.
|
||||
///
|
||||
/// See [`Chunk::call`] for more details.
|
||||
///
|
||||
@@ -2169,7 +2216,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
pub fn into_function(self) -> Result<Function<'lua>> {
|
||||
self.lua
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env, self.mode)
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env()?, self.mode)
|
||||
}
|
||||
|
||||
fn env(&self) -> Result<Option<Value<'lua>>> {
|
||||
match self.env {
|
||||
None => Ok(None),
|
||||
Some(ref env) => env.clone().map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
fn expression_source(&self) -> Vec<u8> {
|
||||
@@ -2180,6 +2234,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
|
||||
fn source(&self) -> &[u8] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
|
||||
#[cfg(feature = "luajit")]
|
||||
// Stop collector during library initialization
|
||||
@@ -2384,7 +2444,6 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
#[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>,
|
||||
@@ -2438,6 +2497,21 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||
self.meta_methods
|
||||
.push((meta.into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: Vec<u8>, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((meta, callback));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
@@ -2450,8 +2524,29 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let userdata = userdata.borrow::<T>()?;
|
||||
method(lua, &userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
match userdata.type_id()? {
|
||||
id if id == TypeId::of::<T>() => {
|
||||
let ud = userdata.borrow::<T>()?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<Mutex<T>>>()?;
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<RwLock<T>>>()?;
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
@@ -2472,11 +2567,34 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let mut userdata = userdata.borrow_mut::<T>()?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
(&mut *method)(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
match userdata.type_id()? {
|
||||
id if id == TypeId::of::<T>() => {
|
||||
let mut ud = userdata.borrow_mut::<T>()?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
|
||||
let mut ud = ud
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<Mutex<T>>>()?;
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<RwLock<T>>>()?;
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
@@ -2659,4 +2777,51 @@ impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFiel
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
fn add_field_getter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_setter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.field_setters.push((name, callback));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
impl<T: 'static + UserData> UserData for $type {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
let mut orig_fields = StaticUserDataFields::default();
|
||||
T::add_fields(&mut orig_fields);
|
||||
for (name, callback) in orig_fields.field_getters {
|
||||
fields.add_field_getter(name, callback);
|
||||
}
|
||||
for (name, callback) in orig_fields.field_setters {
|
||||
fields.add_field_setter(name, callback);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = StaticUserDataMethods::default();
|
||||
T::add_methods(&mut orig_methods);
|
||||
for (name, callback) in orig_methods.methods {
|
||||
methods.add_callback(name, callback);
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (name, callback) in orig_methods.async_methods {
|
||||
methods.add_async_callback(name, callback);
|
||||
}
|
||||
for (meta, callback) in orig_methods.meta_methods {
|
||||
methods.add_meta_callback(meta, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>>);
|
||||
lua_userdata_impl!(Arc<Mutex<T>>);
|
||||
lua_userdata_impl!(Arc<RwLock<T>>);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
+10
-13
@@ -1,8 +1,9 @@
|
||||
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::rc::Rc;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
@@ -238,7 +239,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,7 +248,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
data: UserDataCell<T>,
|
||||
data: Rc<RefCell<T>>,
|
||||
data_ptr: *mut c_void,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
@@ -263,7 +264,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
lua.push_userdata_ref(&ud.0, false)?;
|
||||
if get_userdata(lua.state, -1) == data_ptr {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -276,10 +277,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 +291,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,7 +321,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 13)?;
|
||||
|
||||
push_userdata(lua.state, data.clone())?;
|
||||
push_userdata(lua.state, UserDataCell::new(data.clone()))?;
|
||||
let data_ptr = ffi::lua_touserdata(lua.state, -1);
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
@@ -379,7 +376,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
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,
|
||||
@@ -427,7 +424,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()
|
||||
@@ -665,7 +663,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>,
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
+3
-3
@@ -243,10 +243,10 @@ impl<'lua> Table<'lua> {
|
||||
/// 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)?;
|
||||
@@ -378,7 +378,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.
|
||||
///
|
||||
|
||||
@@ -7,6 +7,9 @@ use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, check_stack, 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::{
|
||||
@@ -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.
|
||||
|
||||
+201
-93
@@ -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;
|
||||
@@ -19,9 +19,15 @@ use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
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 +104,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 +190,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 +252,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 +359,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 +418,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,6 +503,16 @@ 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.
|
||||
@@ -547,26 +589,11 @@ 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> {
|
||||
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")]
|
||||
@@ -574,40 +601,135 @@ impl<T> UserDataCell<T> {
|
||||
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.
|
||||
fn try_borrow(&self) -> Result<UserDataRef<T>> {
|
||||
self.0
|
||||
.try_borrow()
|
||||
.map(|r| UserDataRef(UserDataRefInner::Ref(r)))
|
||||
.map_err(|_| Error::UserDataBorrowError)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value.
|
||||
fn try_borrow_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.0
|
||||
.try_borrow_mut()
|
||||
.map(|r| UserDataRefMut(UserDataRefMutInner::Ref(r)))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataCell<T> {
|
||||
type Target = RefCell<UserDataWrapped<T>>;
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataCell<()> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let ser = self
|
||||
.0
|
||||
.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
|
||||
.ser;
|
||||
unsafe { (&*ser).serialize(serializer) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRef<'a, T>(UserDataRefInner<'a, T>);
|
||||
|
||||
enum UserDataRefInner<'a, T> {
|
||||
Ref(Ref<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRefMut<'a, T>(UserDataRefMutInner<'a, T>);
|
||||
|
||||
enum UserDataRefMutInner<'a, T> {
|
||||
Ref(RefMut<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => &*t,
|
||||
UserDataCell::Plain(t) => &*t,
|
||||
match &self.0 {
|
||||
UserDataRefInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
impl<T> Deref for UserDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match &mut self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &mut *x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
fn new(data: T) -> Self {
|
||||
UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
@@ -620,20 +742,22 @@ impl<T> Drop for UserDataWrapped<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
impl<T> Deref for UserDataWrapped<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct UserDataSerializeError;
|
||||
struct UserDataSerializeError;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataSerializeError {
|
||||
@@ -680,26 +804,18 @@ 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`.
|
||||
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 }))
|
||||
})
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<UserDataRef<T>> {
|
||||
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`.
|
||||
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 }))
|
||||
})
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
@@ -723,7 +839,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
lua.push_value(v)?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
|
||||
@@ -742,7 +858,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
lua.pop_value()
|
||||
};
|
||||
@@ -758,7 +874,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,38 +883,13 @@ 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;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
|
||||
Ok(Table(lua.pop_ref()))
|
||||
}
|
||||
@@ -825,6 +916,25 @@ impl<'lua> AnyUserData<'lua> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub(crate) fn type_id(&self) -> Result<TypeId> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
// Push userdata with metatable
|
||||
lua.push_userdata_ref(&self.0, true)?;
|
||||
|
||||
// Get the special `__mlua_type_id`
|
||||
ffi::safe::lua_pushstring(lua.state, "__mlua_type_id")?;
|
||||
if ffi::lua_rawget(lua.state, -2) != ffi::LUA_TUSERDATA {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
|
||||
Ok(*(ffi::lua_touserdata(lua.state, -1) as *const TypeId))
|
||||
}
|
||||
}
|
||||
|
||||
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
@@ -950,17 +1060,15 @@ 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)?;
|
||||
lua.push_userdata_ref(&self.0, false)
|
||||
.map_err(ser::Error::custom)?;
|
||||
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)
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
-28
@@ -1,5 +1,6 @@
|
||||
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::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
@@ -44,7 +45,7 @@ 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.
|
||||
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||
@@ -323,45 +324,30 @@ where
|
||||
ptr::write(error_ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||
get_gc_metatable_for::<WrappedPanic>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
-1
|
||||
-2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// A part of the C shim for errors handling.
|
||||
// Receives indexes of error and traceback (optional) in the stack.
|
||||
// Converts error into a `CallbackError` and attaches the traceback provided.
|
||||
#[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,
|
||||
traceback_idx: c_int,
|
||||
) {
|
||||
let error = mlua_expect!(
|
||||
get_wrapped_error(state, error_idx).as_ref(),
|
||||
let wrapped_error = mlua_expect!(
|
||||
get_gc_userdata::<WrappedError>(state, error_idx).as_mut(),
|
||||
"cannot get <WrappedError>"
|
||||
);
|
||||
let traceback = if has_traceback != 0 {
|
||||
let traceback = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_owned()
|
||||
let traceback = match traceback_idx {
|
||||
0 => "<not enough stack space for traceback>".to_string(),
|
||||
_ => to_string(state, traceback_idx),
|
||||
};
|
||||
|
||||
let error = error.clone();
|
||||
ffi::lua_remove(state, -2); // Remove original error
|
||||
|
||||
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);
|
||||
let cause = Arc::new(wrapped_error.0.clone());
|
||||
wrapped_error.0 = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
|
||||
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
||||
@@ -475,6 +461,29 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
|
||||
// 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)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -15,6 +15,19 @@ 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()
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
|
||||
@@ -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?"[..]
|
||||
|
||||
@@ -93,6 +93,49 @@ fn test_thread() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
#[test]
|
||||
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 coroutine_from_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+54
-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};
|
||||
@@ -451,3 +454,53 @@ 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"))]
|
||||
{
|
||||
globals.set("rc_refcell_ud", Rc::new(RefCell::new(MyUserData(1))))?;
|
||||
lua.load(
|
||||
r#"
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
}
|
||||
|
||||
globals.set("arc_mutex_ud", Arc::new(Mutex::new(MyUserData(2))))?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
globals.set("arc_rwlock_ud", Arc::new(RwLock::new(MyUserData(3))))?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user