Compare commits

...

30 Commits

Author SHA1 Message Date
Alex Orlenko 5acf9d758d Update CHANGELOG for v0.10.0-beta.1 2024-07-31 13:42:39 +01:00
Alex Orlenko 833790967b Update async examples (use send feature) 2024-07-31 13:42:39 +01:00
Alex Orlenko a86d6ab330 Mark LightUserData as Send+Sync (send feature flag) 2024-07-31 13:42:39 +01:00
Alex Orlenko 2f8755dcc7 Update README 2024-07-31 13:42:38 +01:00
Alex Orlenko 94415065c0 Fix Lua String soundness when borrowing &str or &[u8].
Make borrowing using new `BorrowedStr` and `BorrowedBytes` types that holds strong reference to Lua.
2024-07-31 13:42:38 +01:00
Alex Orlenko bba644e83f Fix compilation 2024-07-31 13:42:38 +01:00
Alex Orlenko d5173380e3 Split util module
Refactor internal userdata types to switch to the new `TypeKey` trait
2024-07-31 13:42:38 +01:00
Alex Orlenko d9941ef409 Take &mut RegistryKey in Lua::replace_registry_value. 2024-07-31 13:42:38 +01:00
Alex Orlenko c715aec1f7 Do not consume self in Table::pairs and Table::sequence_values methods. 2024-07-31 13:42:38 +01:00
Alex Orlenko cd3f45f31f Rust nightly rustfmt 2024-07-31 13:42:37 +01:00
Alex Orlenko b4892c228c Add rustfmt.toml 2024-07-31 13:42:37 +01:00
Alex Orlenko 7a75c73052 Move AppData to types::app_data mod 2024-07-31 13:42:37 +01:00
Alex Orlenko 658f2a13ea Support multi threads under send feature flag 2024-07-31 13:42:37 +01:00
Alex Orlenko c1395ab543 clippy 2024-07-31 13:42:37 +01:00
Alex Orlenko cd6d86a5ce Split single lua module to multiple submodules under state 2024-07-31 13:42:37 +01:00
Alex Orlenko 8b2d067196 Fix Lua::set_vector_metatable 2024-07-31 13:42:36 +01:00
Alex Orlenko 07a5538e50 Don't run destructors if Lua is gone 2024-07-31 13:42:36 +01:00
Alex Orlenko d17dc54645 Rewrite call_async in traits to use impl Future instead of LocalBoxFuture.
More use `std::future` types instead of `future-utils`.
2024-07-31 13:42:36 +01:00
Alex Orlenko baa8895cfb Optimize creation of userdata callbacks (registry) 2024-07-31 13:42:36 +01:00
Alex Orlenko 550d6b2991 Remove wrapped UserData impl (Rc/Arc/etc) 2024-07-31 13:42:36 +01:00
Alex Orlenko b3649a44e0 Move userdata-related top-level modules into the userdata module 2024-07-31 13:42:36 +01:00
Alex Orlenko 313117095c Remove parking_lot feature flag 2024-07-31 13:42:35 +01:00
Alex Orlenko 7221051683 Refactor:
- Remove the rest of `'lua` lifetimes
- Remove Owned types
2024-07-31 13:42:35 +01:00
Alex Orlenko 1eb2ecb3b0 Drop lifetime from IntoLua 2024-07-31 13:42:35 +01:00
Alex Orlenko 24b0672d99 Rename LuaRef to ValueRef 2024-07-31 13:42:35 +01:00
Alex Orlenko 08c7429531 Optimize various parts of the code to remove unnecessary clone calls.
This is became possible after implementing `IntoLua` for references.
2024-07-31 13:42:35 +01:00
Alex Orlenko aa05eb4c81 Switch MultiValue to use VecDeque under the hood. 2024-07-31 13:42:35 +01:00
Alex Orlenko 4aa178fcc0 Take &Value in Lua::push_value() 2024-07-31 13:42:35 +01:00
Alex Orlenko 98ca880f8a Change Value::Error variant to store Box<Error> instead of Error.
This will help to reduce size of `Value` enum on the stack.
2024-07-31 13:42:34 +01:00
Alex Orlenko fe21ef43ba Replace AtomicPtr with Cell 2024-07-31 13:42:34 +01:00
84 changed files with 7784 additions and 8822 deletions
+12 -13
View File
@@ -27,7 +27,7 @@ jobs:
- name: Build ${{ matrix.lua }} vendored
run: |
cargo build --features "${{ matrix.lua }},vendored"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-22.04' }}
@@ -50,7 +50,7 @@ jobs:
toolchain: stable
target: aarch64-apple-darwin
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
@@ -71,7 +71,7 @@ jobs:
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
shell: bash
- name: Cross-compile
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
shell: bash
build_armv7_cross_ubuntu:
@@ -93,7 +93,7 @@ jobs:
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
shell: bash
- name: Cross-compile
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
shell: bash
test:
@@ -122,14 +122,14 @@ jobs:
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --features "${{ matrix.lua }},vendored"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,unstable"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
run: |
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable" -- --ignored
shell: bash
test_with_sanitizer:
@@ -153,7 +153,7 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
env:
RUSTFLAGS: -Z sanitizer=address
@@ -226,17 +226,16 @@ jobs:
- name: Run ${{ matrix.lua }} tests
run: |
cargo test --tests --features "${{ matrix.lua }},vendored"
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,unstable"
rustfmt:
name: Rustfmt
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: stable
components: rustfmt
- run: cargo fmt -- --check
@@ -255,4 +254,4 @@ jobs:
- uses: giraffate/clippy-action@v1
with:
reporter: 'github-pr-review'
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
+9
View File
@@ -1,3 +1,12 @@
## v0.10.0-beta.1
- Dropped `'lua` lifetime (subtypes now store a weak reference to Lua)
- Removed (experimental) owned types (they no longer needed)
- Make Lua types truly `Send` and `Sync` (when enabling `send` feature flag)
- Removed `UserData` impl for Rc/Arc types ("any" userdata functions can be used instead)
- `Lua::replace_registry_value` takes `&mut RegistryKey`
- `Lua::scope` temporary disabled (will be re-added in the next release)
## v0.9.9
- Minimal Luau updated to 0.629
+5 -7
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.9.9" # remember to update mlua_derive
version = "0.10.0-beta.1" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
rust-version = "1.71"
edition = "2021"
@@ -16,7 +16,7 @@ with async/await features and support of writing native Lua modules in Rust.
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot", "unstable"]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "unstable"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
@@ -46,14 +46,13 @@ unstable = []
[dependencies]
mlua_derive = { version = "=0.9.3", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default-features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
rustc-hash = "2.0"
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
serde = { version = "1.0", optional = true }
erased-serde = { version = "0.4", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", optional = true }
parking_lot = { version = "0.12", features = ["arc_lock"] }
ffi = { package = "mlua-sys", version = "0.6.1", path = "mlua-sys" }
@@ -62,7 +61,6 @@ libloading = { version = "0.8", optional = true }
[dev-dependencies]
trybuild = "1.0"
futures = "0.3.5"
hyper = { version = "1.2", features = ["full"] }
hyper-util = { version = "0.1.3", features = ["full"] }
http-body-util = "0.1.1"
@@ -102,11 +100,11 @@ required-features = ["async", "serialize", "macros"]
[[example]]
name = "async_http_server"
required-features = ["async", "macros"]
required-features = ["async", "macros", "send"]
[[example]]
name = "async_tcp_server"
required-features = ["async", "macros"]
required-features = ["async", "macros", "send"]
[[example]]
name = "guided_tour"
+1 -3
View File
@@ -17,9 +17,7 @@
[Benchmarks]: https://github.com/khvzak/script-bench-rs
[FAQ]: FAQ.md
> **Note**
>
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.9.md).
# The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`.
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
+6 -15
View File
@@ -183,9 +183,7 @@ fn function_call_concat(c: &mut Criterion) {
let lua = Lua::new();
let concat = lua
.create_function(|_, (a, b): (LuaString, LuaString)| {
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
})
.create_function(|_, (a, b): (LuaString, LuaString)| Ok(format!("{}{}", a.to_str()?, b.to_str()?)))
.unwrap();
let i = AtomicUsize::new(0);
@@ -305,7 +303,7 @@ fn userdata_create(c: &mut Criterion) {
fn userdata_call_index(c: &mut Criterion) {
struct UserData(#[allow(unused)] i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
}
}
@@ -331,7 +329,7 @@ fn userdata_call_index(c: &mut Criterion) {
fn userdata_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
}
}
@@ -361,7 +359,7 @@ fn userdata_call_method(c: &mut Criterion) {
fn userdata_async_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_async_method("add", |_, this, i: i64| async move {
task::yield_now().await;
Ok(this.0 + i)
@@ -383,17 +381,10 @@ fn userdata_async_call_method(c: &mut Criterion) {
b.to_async(rt).iter_batched(
|| {
collect_gc_twice(&lua);
(
method.clone(),
ud.clone(),
i.fetch_add(1, Ordering::Relaxed),
)
(method.clone(), ud.clone(), i.fetch_add(1, Ordering::Relaxed))
},
|(method, ud, i)| async move {
assert_eq!(
method.call_async::<_, usize>((ud, i)).await.unwrap(),
123 + i
);
assert_eq!(method.call_async::<_, usize>((ud, i)).await.unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
+24 -52
View File
@@ -1,21 +1,17 @@
use std::convert::Infallible;
use std::future::Future;
use std::net::SocketAddr;
use std::rc::Rc;
use std::pin::Pin;
use futures::future::LocalBoxFuture;
use http_body_util::{combinators::BoxBody, BodyExt as _, Empty, Full};
use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt as _, Empty, Full};
use hyper::body::{Bytes, Incoming};
use hyper::server::conn::http1;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use hyper_util::server::conn::auto::Builder as ServerConnBuilder;
use tokio::net::TcpListener;
use tokio::task::LocalSet;
use mlua::{
chunk, Error as LuaError, Function, Lua, RegistryKey, String as LuaString, Table, UserData,
UserDataMethods,
};
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
/// Wrapper around incoming request that implements UserData
struct LuaRequest(SocketAddr, Request<Incoming>);
@@ -31,33 +27,26 @@ impl UserData for LuaRequest {
/// Service that handles incoming requests
#[derive(Clone)]
pub struct Svc {
lua: Rc<Lua>,
handler: Rc<RegistryKey>,
handler: Function,
peer_addr: SocketAddr,
}
impl Svc {
pub fn new(lua: Rc<Lua>, handler: Rc<RegistryKey>, peer_addr: SocketAddr) -> Self {
Self {
lua,
handler,
peer_addr,
}
pub fn new(handler: Function, peer_addr: SocketAddr) -> Self {
Self { handler, peer_addr }
}
}
impl hyper::service::Service<Request<Incoming>> for Svc {
type Response = Response<BoxBody<Bytes, Infallible>>;
type Error = LuaError;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, req: Request<Incoming>) -> Self::Future {
// If handler returns an error then generate 5xx response
let lua = self.lua.clone();
let handler_key = self.handler.clone();
let handler = self.handler.clone();
let lua_req = LuaRequest(self.peer_addr, req);
Box::pin(async move {
let handler: Function = lua.registry_value(&handler_key)?;
match handler.call_async::<_, Table>(lua_req).await {
Ok(lua_resp) => {
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
@@ -67,14 +56,14 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
if let Some(headers) = lua_resp.get::<_, Option<Table>>("headers")? {
for pair in headers.pairs::<String, LuaString>() {
let (h, v) = pair?;
resp = resp.header(&h, v.as_bytes());
resp = resp.header(&h, &*v.as_bytes());
}
}
// Set body
let body = lua_resp
.get::<_, Option<LuaString>>("body")?
.map(|b| Full::new(Bytes::copy_from_slice(b.as_bytes())).boxed())
.map(|b| Full::new(Bytes::copy_from_slice(&b.as_bytes())).boxed())
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
Ok(resp.body(body).unwrap())
@@ -93,10 +82,10 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
#[tokio::main(flavor = "current_thread")]
async fn main() {
let lua = Rc::new(Lua::new());
let lua = Lua::new();
// Create Lua handler function
let handler: RegistryKey = lua
let handler = lua
.load(chunk! {
function(req)
return {
@@ -110,15 +99,13 @@ async fn main() {
}
end
})
.eval()
.eval::<Function>()
.expect("Failed to create Lua handler");
let handler = Rc::new(handler);
let listen_addr = "127.0.0.1:3000";
let listener = TcpListener::bind(listen_addr).await.unwrap();
println!("Listening on http://{listen_addr}");
let local = LocalSet::new();
loop {
let (stream, peer_addr) = match listener.accept().await {
Ok(x) => x,
@@ -128,29 +115,14 @@ async fn main() {
}
};
let svc = Svc::new(lua.clone(), handler.clone(), peer_addr);
local
.run_until(async move {
let result = ServerConnBuilder::new(LocalExec)
.http1()
.serve_connection(TokioIo::new(stream), svc)
.await;
if let Err(err) = result {
eprintln!("Error serving connection: {err:?}");
}
})
.await;
}
}
#[derive(Clone, Copy, Debug)]
struct LocalExec;
impl<F> hyper::rt::Executor<F> for LocalExec
where
F: Future + 'static, // not requiring `Send`
{
fn execute(&self, fut: F) {
tokio::task::spawn_local(fut);
let svc = Svc::new(handler.clone(), peer_addr);
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(TokioIo::new(stream), svc)
.await
{
eprintln!("Error serving connection: {:?}", err);
}
});
}
}
+6 -25
View File
@@ -1,20 +1,16 @@
use std::io;
use std::net::SocketAddr;
use std::rc::Rc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::task;
use mlua::{chunk, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods};
use mlua::{chunk, Function, Lua, String as LuaString, UserData, UserDataMethods};
struct LuaTcpStream(TcpStream);
impl UserData for LuaTcpStream {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("peer_addr", |_, this, ()| {
Ok(this.0.peer_addr()?.to_string())
});
methods.add_method("peer_addr", |_, this, ()| Ok(this.0.peer_addr()?.to_string()));
methods.add_async_method_mut("read", |lua, this, size| async move {
let mut buf = vec![0; size];
@@ -35,14 +31,12 @@ impl UserData for LuaTcpStream {
}
}
async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
async fn run_server(handler: Function) -> io::Result<()> {
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let listener = TcpListener::bind(addr).await.expect("cannot bind addr");
println!("Listening on {}", addr);
let lua = Rc::new(lua);
let handler = Rc::new(handler);
loop {
let (stream, _) = match listener.accept().await {
Ok(res) => res,
@@ -50,13 +44,8 @@ async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
Err(err) => return Err(err),
};
let lua = lua.clone();
let handler = handler.clone();
task::spawn_local(async move {
let handler: Function = lua
.registry_value(&handler)
.expect("cannot get Lua handler");
tokio::task::spawn(async move {
let stream = LuaTcpStream(stream);
if let Err(err) = handler.call_async::<_, ()>(stream).await {
eprintln!("{}", err);
@@ -70,7 +59,7 @@ async fn main() {
let lua = Lua::new();
// Create Lua handler function
let handler_fn = lua
let handler = lua
.load(chunk! {
function(stream)
local peer_addr = stream:peer_addr()
@@ -92,15 +81,7 @@ async fn main() {
.eval::<Function>()
.expect("cannot create Lua handler");
// Store it in the Registry
let handler = lua
.create_registry_value(handler_fn)
.expect("cannot store Lua handler");
task::LocalSet::new()
.run_until(run_server(lua, handler))
.await
.expect("cannot run server")
run_server(handler).await.expect("cannot run server")
}
fn is_transient_error(e: &io::Error) -> bool {
+7 -12
View File
@@ -1,9 +1,7 @@
use std::f32;
use std::iter::FromIterator;
use mlua::{
chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic,
};
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
fn main() -> Result<()> {
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
@@ -154,8 +152,8 @@ fn main() -> Result<()> {
struct Vec2(f32, f32);
// We can implement `FromLua` trait for our `Vec2` to return a copy
impl<'lua> FromLua<'lua> for Vec2 {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
impl FromLua for Vec2 {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => unreachable!(),
@@ -179,19 +177,15 @@ fn main() -> Result<()> {
let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y)))?;
globals.set("vec2", vec2_constructor)?;
assert!(
(lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()")
.eval::<f32>()?
- 5.0)
.abs()
< f32::EPSILON
);
assert!((lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()").eval::<f32>()? - 5.0).abs() < f32::EPSILON);
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
// TODO: Re-enable this
/*
{
let mut rust_val = 0;
@@ -213,6 +207,7 @@ fn main() -> Result<()> {
assert_eq!(rust_val, 42);
}
*/
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
// try to run our 'sketchy' function outside of the scope, the function we created will have
+7 -2
View File
@@ -28,9 +28,14 @@ fn main() -> Result<()> {
let globals = lua.globals();
// Create Car struct from a Lua table
let car: Car = lua.from_value(lua.load(r#"
let car: Car = lua.from_value(
lua.load(
r#"
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
"#).eval()?)?;
"#,
)
.eval()?,
)?;
// Set it as (serializable) userdata
globals.set("null", lua.null())?;
+1 -4
View File
@@ -54,10 +54,7 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
target_arch = "sparc",
target_arch = "wasm32",
target_arch = "hexagon",
all(
target_arch = "riscv32",
not(any(target_os = "espidf", target_os = "zkvm"))
),
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
all(target_arch = "xtensa", not(target_os = "espidf")),
))]
#[doc(hidden)]
+5 -26
View File
@@ -2,9 +2,8 @@
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::{mem, ptr};
use super::lauxlib::*;
use super::lua::*;
@@ -328,12 +327,7 @@ pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
}
#[inline(always)]
pub unsafe fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
data: *mut c_void,
_strip: c_int,
) -> c_int {
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
lua_dump_(L, writer, data)
}
@@ -365,12 +359,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
}
#[inline(always)]
pub unsafe fn lua_resume(
L: *mut lua_State,
_from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int {
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
@@ -446,12 +435,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
res
}
pub unsafe fn luaL_traceback(
L: *mut lua_State,
L1: *mut lua_State,
msg: *const c_char,
mut level: c_int,
) {
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
let mut ar: lua_Debug = mem::zeroed();
let top = lua_gettop(L);
let numlevels = compat53_countlevels(L1);
@@ -543,12 +527,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
0
}
pub unsafe fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
) {
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
if lua_getfield(L, -1, modname) == LUA_TNIL {
+1 -6
View File
@@ -63,12 +63,7 @@ extern "C-unwind" {
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
pub fn luaL_loadbuffer(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
) -> c_int;
pub fn luaL_loadbuffer(L: *mut lua_State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
+1 -6
View File
@@ -379,12 +379,7 @@ extern "C-unwind" {
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_sethook(
L: *mut lua_State,
func: Option<lua_Hook>,
mask: c_int,
count: c_int,
) -> c_int;
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
+3 -18
View File
@@ -157,22 +157,12 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
}
#[inline(always)]
pub unsafe fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
data: *mut c_void,
_strip: c_int,
) -> c_int {
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
lua_dump_(L, writer, data)
}
#[inline(always)]
pub unsafe fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int {
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, from, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
@@ -240,12 +230,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
lua_tolstring(L, -1, len)
}
pub unsafe fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
) {
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
if lua_getfield(L, -1, modname) == LUA_TNIL {
+5 -20
View File
@@ -25,12 +25,8 @@ extern "C-unwind" {
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
-> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
@@ -71,8 +67,7 @@ extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
-> c_int;
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}
#[inline(always)]
@@ -109,12 +104,7 @@ extern "C-unwind" {
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
#[link_name = "luaL_requiref"]
pub fn luaL_requiref_(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
);
pub fn luaL_requiref_(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}
//
@@ -173,12 +163,7 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
s: *const c_char,
sz: usize,
n: *const c_char,
) -> c_int {
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
+3 -19
View File
@@ -222,13 +222,7 @@ extern "C-unwind" {
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_callk(
L: *mut lua_State,
nargs: c_int,
nresults: c_int,
ctx: c_int,
k: Option<lua_CFunction>,
);
pub fn lua_callk(L: *mut lua_State, nargs: c_int, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>);
pub fn lua_pcallk(
L: *mut lua_State,
nargs: c_int,
@@ -266,12 +260,7 @@ extern "C-unwind" {
//
// Coroutine functions
//
pub fn lua_yieldk(
L: *mut lua_State,
nresults: c_int,
ctx: c_int,
k: Option<lua_CFunction>,
) -> c_int;
pub fn lua_yieldk(L: *mut lua_State, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>) -> c_int;
#[link_name = "lua_resume"]
pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
@@ -471,12 +460,7 @@ extern "C-unwind" {
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
pub fn lua_sethook(
L: *mut lua_State,
func: Option<lua_Hook>,
mask: c_int,
count: c_int,
) -> c_int;
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
+1 -6
View File
@@ -5,12 +5,7 @@ use std::os::raw::c_int;
use super::lua::*;
#[inline(always)]
pub unsafe fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int {
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, from, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
+5 -20
View File
@@ -30,12 +30,8 @@ extern "C-unwind" {
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
-> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
@@ -73,8 +69,7 @@ extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
-> c_int;
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}
#[inline(always)]
@@ -110,12 +105,7 @@ extern "C-unwind" {
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
pub fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
);
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}
//
@@ -179,12 +169,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
s: *const c_char,
sz: usize,
n: *const c_char,
) -> c_int {
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
+2 -8
View File
@@ -1,9 +1,8 @@
//! Contains definitions from `lua.h`.
use std::marker::{PhantomData, PhantomPinned};
use std::mem;
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_void};
use std::ptr;
use std::{mem, ptr};
// Mark for precompiled code (`<esc>Lua`)
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
@@ -251,12 +250,7 @@ extern "C-unwind" {
mode: *const c_char,
) -> c_int;
pub fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
data: *mut c_void,
strip: c_int,
) -> c_int;
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
}
#[inline(always)]
+5 -20
View File
@@ -29,12 +29,8 @@ extern "C-unwind" {
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
arg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
-> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
@@ -72,8 +68,7 @@ extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
-> c_int;
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}
#[inline(always)]
@@ -111,12 +106,7 @@ extern "C-unwind" {
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
pub fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
);
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}
//
@@ -175,12 +165,7 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
// luaL_opt would be implemented here but it is undocumented, so it's omitted
#[inline(always)]
pub unsafe fn luaL_loadbuffer(
L: *mut lua_State,
s: *const c_char,
sz: usize,
n: *const c_char,
) -> c_int {
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
luaL_loadbufferx(L, s, sz, n, ptr::null())
}
+4 -16
View File
@@ -1,9 +1,8 @@
//! Contains definitions from `lua.h`.
use std::marker::{PhantomData, PhantomPinned};
use std::mem;
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, c_void};
use std::ptr;
use std::{mem, ptr};
// Mark for precompiled code (`<esc>Lua`)
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
@@ -101,8 +100,7 @@ pub type lua_Alloc =
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
/// Type for warning functions
pub type lua_WarnFunction =
unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
pub type lua_WarnFunction = unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
extern "C-unwind" {
@@ -266,12 +264,7 @@ extern "C-unwind" {
mode: *const c_char,
) -> c_int;
pub fn lua_dump(
L: *mut lua_State,
writer: lua_Writer,
data: *mut c_void,
strip: c_int,
) -> c_int;
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
}
#[inline(always)]
@@ -295,12 +288,7 @@ extern "C-unwind" {
ctx: lua_KContext,
k: Option<lua_KFunction>,
) -> c_int;
pub fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int;
pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
}
+7 -35
View File
@@ -3,9 +3,8 @@
//! Based on github.com/keplerproject/lua-compat-5.3
use std::ffi::CStr;
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::{mem, ptr};
use super::lauxlib::*;
use super::lua::*;
@@ -53,11 +52,7 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
0 // not found
}
unsafe fn compat53_pushglobalfuncname(
L: *mut lua_State,
level: c_int,
ar: *mut lua_Debug,
) -> c_int {
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int {
let top = lua_gettop(L);
// push function
lua_getinfo(L, level, cstr!("f"), ar);
@@ -281,12 +276,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
}
#[inline(always)]
pub unsafe fn lua_resume(
L: *mut lua_State,
from: *mut lua_State,
narg: c_int,
nres: *mut c_int,
) -> c_int {
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, from, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
@@ -345,18 +335,10 @@ pub unsafe fn luaL_loadbufferx(
if !mode.is_null() {
let modeb = CStr::from_ptr(mode).to_bytes();
if !chunk_is_text && !modeb.contains(&b'b') {
lua_pushfstring(
L,
cstr!("attempt to load a binary chunk (mode is '%s')"),
mode,
);
lua_pushfstring(L, cstr!("attempt to load a binary chunk (mode is '%s')"), mode);
return LUA_ERRSYNTAX;
} else if chunk_is_text && !modeb.contains(&b't') {
lua_pushfstring(
L,
cstr!("attempt to load a text chunk (mode is '%s')"),
mode,
);
lua_pushfstring(L, cstr!("attempt to load a text chunk (mode is '%s')"), mode);
return LUA_ERRSYNTAX;
}
}
@@ -397,12 +379,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
res
}
pub unsafe fn luaL_traceback(
L: *mut lua_State,
L1: *mut lua_State,
msg: *const c_char,
mut level: c_int,
) {
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
let mut ar: lua_Debug = mem::zeroed();
let top = lua_gettop(L);
let numlevels = lua_stackdepth(L);
@@ -494,12 +471,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
0
}
pub unsafe fn luaL_requiref(
L: *mut lua_State,
modname: *const c_char,
openf: lua_CFunction,
glb: c_int,
) {
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
if lua_getfield(L, -1, modname) == LUA_TNIL {
+1 -3
View File
@@ -3,9 +3,7 @@
use std::os::raw::{c_char, c_float, c_int, c_void};
use std::ptr;
use super::lua::{
self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX,
};
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
#[repr(C)]
pub struct luaL_Reg {
+6 -30
View File
@@ -84,12 +84,8 @@ pub type lua_Udestructor = unsafe extern "C-unwind" fn(*mut c_void);
pub type lua_Destructor = unsafe extern "C-unwind" fn(L: *mut lua_State, *mut c_void);
/// Type for memory-allocation functions.
pub type lua_Alloc = unsafe extern "C-unwind" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
pub type lua_Alloc =
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
/// Returns Luau release version (eg. `0.xxx`).
pub const fn luau_version() -> Option<&'static str> {
@@ -426,12 +422,7 @@ pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int)
}
#[inline(always)]
pub unsafe fn lua_pushcclosured(
L: *mut lua_State,
f: lua_CFunction,
debugname: *const c_char,
nup: c_int,
) {
pub unsafe fn lua_pushcclosured(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char, nup: c_int) {
lua_pushcclosurek(L, f, debugname, nup, None)
}
@@ -476,12 +467,7 @@ pub type lua_Coverage = unsafe extern "C-unwind" fn(
extern "C-unwind" {
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
pub fn lua_getinfo(
L: *mut lua_State,
level: c_int,
what: *const c_char,
ar: *mut lua_Debug,
) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getargument(L: *mut lua_State, level: c_int, n: c_int) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
@@ -489,19 +475,9 @@ extern "C-unwind" {
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
pub fn lua_breakpoint(
L: *mut lua_State,
funcindex: c_int,
line: c_int,
enabled: c_int,
) -> c_int;
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int) -> c_int;
pub fn lua_getcoverage(
L: *mut lua_State,
funcindex: c_int,
context: *mut c_void,
callback: lua_Coverage,
);
pub fn lua_getcoverage(L: *mut lua_State, funcindex: c_int, context: *mut c_void, callback: lua_Coverage);
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
}
+1 -2
View File
@@ -1,7 +1,6 @@
#[allow(unused_macros)]
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
as *const ::std::os::raw::c_char
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
};
}
+3 -5
View File
@@ -3,9 +3,7 @@ use quote::quote;
use syn::{parse_macro_input, DeriveInput};
pub fn from_lua(input: TokenStream) -> TokenStream {
let DeriveInput {
ident, generics, ..
} = parse_macro_input!(input as DeriveInput);
let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
let ident_str = ident.to_string();
let (impl_generics, ty_generics, _) = generics.split_for_impl();
@@ -15,9 +13,9 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
};
quote! {
impl #impl_generics ::mlua::FromLua<'_> for #ident #ty_generics #where_clause {
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
#[inline]
fn from_lua(value: ::mlua::Value<'_>, _: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
match value {
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => Err(::mlua::Error::FromLuaConversionError {
+7 -10
View File
@@ -99,15 +99,14 @@ pub fn chunk(input: TokenStream) -> TokenStream {
use ::std::borrow::Cow;
use ::std::cell::Cell;
use ::std::io::Result as IoResult;
use ::std::marker::PhantomData;
struct InnerChunk<'lua, F: FnOnce(&'lua Lua) -> Result<Table<'lua>>>(Cell<Option<F>>, PhantomData<&'lua ()>);
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
impl<'lua, F> AsChunk<'lua, 'static> for InnerChunk<'lua, F>
impl<F> AsChunk<'static> for InnerChunk<F>
where
F: FnOnce(&'lua Lua) -> Result<Table<'lua>>,
F: FnOnce(&Lua) -> Result<Table>,
{
fn environment(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
if #caps_len > 0 {
if let Some(make_env) = self.0.take() {
return make_env(lua).map(Some);
@@ -125,9 +124,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
}
}
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Table<'a>>>(f: F) -> F { f }
let make_env = annotate(move |lua: &Lua| -> Result<Table> {
let make_env = move |lua: &Lua| -> Result<Table> {
let globals = lua.globals();
let env = lua.create_table()?;
let meta = lua.create_table()?;
@@ -139,9 +136,9 @@ pub fn chunk(input: TokenStream) -> TokenStream {
env.set_metatable(Some(meta));
Ok(env)
});
};
InnerChunk(Cell::new(Some(make_env)), PhantomData)
InnerChunk(Cell::new(Some(make_env)))
}};
wrapped_code.into()
+5 -12
View File
@@ -1,8 +1,6 @@
use std::{
cmp::{Eq, PartialEq},
fmt::{self, Display, Formatter},
vec::IntoIter,
};
use std::cmp::{Eq, PartialEq};
use std::fmt::{self, Display, Formatter};
use std::vec::IntoIter;
use itertools::Itertools;
use once_cell::sync::Lazy;
@@ -47,10 +45,7 @@ fn span_pos(span: &Span) -> (Pos, Pos) {
return fallback_span_pos(span);
}
(
Pos::new(start.line, start.column),
Pos::new(end.line, end.column),
)
(Pos::new(start.line, start.column), Pos::new(end.line, end.column))
}
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
@@ -79,9 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
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"
),
None => proc_macro_error::abort_call_site!("Cannot retrieve span information; please use nightly"),
};
(Pos::new(1, start), Pos::new(1, end))
}
+4
View File
@@ -0,0 +1,4 @@
imports_granularity = "Module"
max_width = 110
comment_width = 100
wrap_comments = true
+39 -36
View File
@@ -7,7 +7,7 @@ use std::string::String as StdString;
use crate::error::{Error, ErrorContext, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::state::{Lua, WeakLua};
use crate::table::Table;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
@@ -15,7 +15,7 @@ use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
///
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
/// [`Chunk`]: crate::Chunk
pub trait AsChunk<'lua, 'a> {
pub trait AsChunk<'a> {
/// Returns optional chunk name
fn name(&self) -> Option<StdString> {
None
@@ -24,7 +24,7 @@ pub trait AsChunk<'lua, 'a> {
/// Returns optional chunk [environment]
///
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
fn environment(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
let _lua = lua; // suppress warning
Ok(None)
}
@@ -38,43 +38,43 @@ pub trait AsChunk<'lua, 'a> {
fn source(self) -> IoResult<Cow<'a, [u8]>>;
}
impl<'a> AsChunk<'_, 'a> for &'a str {
impl<'a> AsChunk<'a> for &'a str {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl AsChunk<'_, 'static> for StdString {
impl AsChunk<'static> for StdString {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self.into_bytes()))
}
}
impl<'a> AsChunk<'_, 'a> for &'a StdString {
impl<'a> AsChunk<'a> for &'a StdString {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_bytes()))
}
}
impl<'a> AsChunk<'_, 'a> for &'a [u8] {
impl<'a> AsChunk<'a> for &'a [u8] {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl AsChunk<'_, 'static> for Vec<u8> {
impl AsChunk<'static> for Vec<u8> {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self))
}
}
impl<'a> AsChunk<'_, 'a> for &'a Vec<u8> {
impl<'a> AsChunk<'a> for &'a Vec<u8> {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl AsChunk<'_, 'static> for &Path {
impl AsChunk<'static> for &Path {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
@@ -84,7 +84,7 @@ impl AsChunk<'_, 'static> for &Path {
}
}
impl AsChunk<'_, 'static> for PathBuf {
impl AsChunk<'static> for PathBuf {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
@@ -98,10 +98,10 @@ impl AsChunk<'_, 'static> for PathBuf {
///
/// [`Lua::load`]: crate::Lua::load
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
pub(crate) lua: &'lua Lua,
pub struct Chunk<'a> {
pub(crate) lua: WeakLua,
pub(crate) name: StdString,
pub(crate) env: Result<Option<Table<'lua>>>,
pub(crate) env: Result<Option<Table>>,
pub(crate) mode: Option<ChunkMode>,
pub(crate) source: IoResult<Cow<'a, [u8]>>,
#[cfg(feature = "luau")]
@@ -290,7 +290,7 @@ impl Compiler {
}
}
impl<'lua, 'a> Chunk<'lua, 'a> {
impl<'a> Chunk<'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
@@ -299,8 +299,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the environment of the loaded chunk to the given value.
///
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
/// variable inside the chunk. By default this value is set to the global environment.
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the
/// `_ENV` variable inside the chunk. By default this value is set to the global environment.
///
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
/// the chunk will refer to the given environment rather than the global one.
@@ -308,10 +308,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
/// necessary to populate the environment in order for scripts using custom environments to be
/// useful.
pub fn set_environment<V: IntoLua<'lua>>(mut self, env: V) -> Self {
pub fn set_environment<V: IntoLua>(mut self, env: V) -> Self {
let lua = self.lua.lock();
let lua = lua.lua();
self.env = env
.into_lua(self.lua)
.and_then(|val| self.lua.unpack(val))
.into_lua(lua)
.and_then(|val| lua.unpack(val))
.context("bad environment value");
self
}
@@ -363,7 +365,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
/// and this is equivalent to calling `exec`.
pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> {
pub fn eval<R: FromLuaMulti>(self) -> Result<R> {
// Bytecode is always interpreted as a statement.
// For source code, first try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
@@ -388,7 +390,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn eval_async<R>(self) -> Result<R>
where
R: FromLuaMulti<'lua> + 'lua,
R: FromLuaMulti,
{
if self.detect_mode() == ChunkMode::Binary {
self.call_async(()).await
@@ -402,7 +404,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Load the chunk function and call it with the given arguments.
///
/// This is equivalent to `into_function` and calling the resulting function.
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
pub fn call<A: IntoLuaMulti, R: FromLuaMulti>(self, args: A) -> Result<R> {
self.into_function()?.call(args)
}
@@ -417,8 +419,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn call_async<A, R>(self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.into_function()?.call_async(args).await
}
@@ -427,7 +429,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
///
/// This simply compiles the chunk without actually executing it.
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
pub fn into_function(mut self) -> Result<Function<'lua>> {
pub fn into_function(mut self) -> Result<Function> {
#[cfg(feature = "luau")]
if self.compiler.is_some() {
// We don't need to compile source if no compiler set
@@ -436,6 +438,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
let name = Self::convert_name(self.name)?;
self.lua
.lock()
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
}
@@ -447,15 +450,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
if self.detect_mode() == ChunkMode::Text {
#[cfg(feature = "luau")]
{
let data = self
.compiler
.get_or_insert_with(Default::default)
.compile(source);
let data = self.compiler.get_or_insert_with(Default::default).compile(source);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
#[cfg(not(feature = "luau"))]
if let Ok(func) = self.lua.load_chunk(None, None, None, source.as_ref()) {
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
let data = func.dump(false);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
@@ -474,7 +474,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
let mut text_source = None;
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
if let Some(cache) = self.lua.app_data_ref::<ChunksCache>() {
let lua = self.lua.lock();
if let Some(cache) = lua.app_data_ref::<ChunksCache>() {
if let Some(data) = cache.0.get(source.as_ref()) {
self.source = Ok(Cow::Owned(data.clone()));
self.mode = Some(ChunkMode::Binary);
@@ -490,13 +491,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self.compile();
if let Ok(ref binary_source) = self.source {
if self.detect_mode() == ChunkMode::Binary {
if let Some(mut cache) = self.lua.app_data_mut::<ChunksCache>() {
let lua = self.lua.lock();
if let Some(mut cache) = lua.app_data_mut::<ChunksCache>() {
cache.0.insert(text_source, binary_source.as_ref().to_vec());
} else {
let mut cache = ChunksCache(HashMap::new());
cache.0.insert(text_source, binary_source.as_ref().to_vec());
let _ = self.lua.try_set_app_data(cache);
}
let _ = lua.try_set_app_data(cache);
};
}
}
}
@@ -504,7 +506,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self
}
fn to_expression(&self) -> Result<Function<'lua>> {
fn to_expression(&self) -> Result<Function> {
// We assume that mode is Text
let source = self.source.as_ref();
let source = source.map_err(Error::runtime)?;
@@ -519,6 +521,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
let name = Self::convert_name(self.name.clone())?;
self.lua
.lock()
.load_chunk(Some(&name), self.env.clone()?, None, &source)
}
+190 -388
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -324,7 +324,8 @@ impl StdError for Error {
// An error type with a source error should either return that error via source or
// include that source's error message in its own Display output, but never both.
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
// returns nothing.
Error::CallbackError { .. } => None,
Error::ExternalError(ref err) => err.source(),
Error::WithContext { ref cause, .. } => match cause.as_ref() {
+79 -134
View File
@@ -1,49 +1,25 @@
use std::cell::RefCell;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use std::{mem, ptr, slice};
use crate::error::{Error, Result};
use crate::lua::Lua;
use crate::state::Lua;
use crate::table::Table;
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::types::{Callback, MaybeSend, ValueRef};
use crate::util::{
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
StackGuard,
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
futures_util::future::{self, Future},
std::future::{self, Future},
};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua function.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedFunction(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedFunction {
/// Get borrowed handle to the underlying Lua function.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Function {
Function(self.0.to_ref())
}
}
pub struct Function(pub(crate) ValueRef);
/// Contains information about a function.
///
@@ -58,7 +34,8 @@ pub struct FunctionInfo {
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
/// the main part of a chunk.
pub what: &'static str,
/// Source of the chunk that created the function.
pub source: Option<String>,
@@ -81,7 +58,7 @@ pub struct CoverageInfo {
pub hits: Vec<i32>,
}
impl<'lua> Function<'lua> {
impl Function {
/// Calls the function, passing `args` as function arguments.
///
/// The function's return values are converted to the generic type `R`.
@@ -122,8 +99,8 @@ impl<'lua> Function<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua;
pub fn call<A: IntoLuaMulti, R: FromLuaMulti>(&self, args: A) -> Result<R> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -134,7 +111,7 @@ impl<'lua> Function<'lua> {
let stack_start = ffi::lua_gettop(state);
// Push function and the arguments
lua.push_ref(&self.0);
let nargs = args.push_into_stack_multi(lua)?;
let nargs = args.push_into_stack_multi(&lua)?;
// Call the function
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK {
@@ -142,7 +119,7 @@ impl<'lua> Function<'lua> {
}
// Get the results
let nresults = ffi::lua_gettop(state) - stack_start;
R::from_stack_multi(nresults, lua)
R::from_stack_multi(nresults, &lua)
}
}
@@ -176,17 +153,19 @@ impl<'lua> Function<'lua> {
/// [`AsyncThread`]: crate::AsyncThread
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>> + 'lua
pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua;
let thread_res = lua.create_recycled_thread(self).map(|th| {
let mut th = th.into_async(args);
th.set_recyclable(true);
th
});
let lua = self.0.lua.lock();
let thread_res = unsafe {
lua.create_recycled_thread(self).map(|th| {
let mut th = th.into_async(args);
th.set_recyclable(true);
th
})
};
async move { thread_res?.await }
}
@@ -217,7 +196,7 @@ impl<'lua> Function<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn bind<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
pub fn bind<A: IntoLuaMulti>(&self, args: A) -> Result<Function> {
unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
@@ -233,10 +212,10 @@ impl<'lua> Function<'lua> {
nargs + nbinds
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
let args = args.into_lua_multi(lua)?;
let args = args.into_lua_multi(lua.lua())?;
let nargs = args.len() as c_int;
if nargs == 0 {
@@ -252,7 +231,7 @@ impl<'lua> Function<'lua> {
check_stack(state, nargs + 3)?;
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
for arg in args {
for arg in &args {
lua.push_value(arg)?;
}
protect_lua!(state, nargs + 1, 1, fn(state) {
@@ -262,6 +241,7 @@ impl<'lua> Function<'lua> {
Function(lua.pop_ref())
};
let lua = lua.lua();
lua.load(
r#"
local func, args_wrapper = ...
@@ -272,7 +252,7 @@ impl<'lua> Function<'lua> {
)
.try_cache()
.set_name("__mlua_bind")
.call((self.clone(), args_wrapper))
.call((self, args_wrapper))
}
/// Returns the environment of the Lua function.
@@ -281,7 +261,7 @@ impl<'lua> Function<'lua> {
///
/// This function always returns `None` for Rust/C functions.
pub fn environment(&self) -> Option<Table> {
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -318,7 +298,7 @@ impl<'lua> Function<'lua> {
///
/// This function does nothing for Rust/C functions.
pub fn set_environment(&self, env: Table) -> Result<bool> {
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -342,6 +322,7 @@ impl<'lua> Function<'lua> {
ffi::lua_pop(state, 1);
// Create an anonymous function with the new environment
let f_with_env = lua
.lua()
.load("return _ENV")
.set_environment(env)
.try_cache()
@@ -364,7 +345,7 @@ impl<'lua> Function<'lua> {
///
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
pub fn info(&self) -> FunctionInfo {
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -425,7 +406,7 @@ impl<'lua> Function<'lua> {
0
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
let mut data: Vec<u8> = Vec::new();
unsafe {
@@ -443,8 +424,8 @@ impl<'lua> Function<'lua> {
/// Retrieves recorded coverage information about this Lua function including inner calls.
///
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
/// per each executed inner function.
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`]
/// snapshot per each executed inner function.
///
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
///
@@ -482,7 +463,7 @@ impl<'lua> Function<'lua> {
});
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -514,134 +495,98 @@ impl<'lua> Function<'lua> {
#[cfg(feature = "luau")]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn deep_clone(&self) -> Self {
let ref_thread = self.0.lua.ref_thread();
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
return self.clone();
}
ffi::lua_clonefunction(ref_thread, self.0.index);
Function(self.0.lua.pop_ref_thread())
Function(lua.pop_ref_thread())
}
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedFunction {
OwnedFunction(self.0.into_owned())
}
}
impl<'lua> PartialEq for Function<'lua> {
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedFunction {
/// Calls the function, passing `args` as function arguments.
///
/// This is a shortcut for [`Function::call()`].
#[inline]
pub fn call<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.to_ref().call(args)
}
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
/// and drives the execution.
///
/// This is a shortcut for [`Function::call_async()`].
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[inline]
pub async fn call_async<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
{
self.to_ref().call_async(args).await
}
}
pub(crate) struct WrappedFunction<'lua>(pub(crate) Callback<'lua, 'static>);
pub(crate) struct WrappedFunction(pub(crate) Callback<'static>);
#[cfg(feature = "async")]
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback<'static>);
impl<'lua> Function<'lua> {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] trait.
impl Function {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
#[inline]
pub fn wrap<A, R, F>(func: F) -> impl IntoLua<'lua>
pub fn wrap<A, R, F>(func: F) -> impl IntoLua
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua, args)?.push_into_stack_multi(lua)
func(lua.lua(), args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua<'lua>
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua, args)?.push_into_stack_multi(lua)
func(lua.lua(), args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] trait.
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua<'lua>
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + 'lua,
A: FromLuaMulti,
R: IntoLuaMulti,
F: Fn(&Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + 'static,
{
WrappedAsyncFunction(Box::new(move |lua, args| unsafe {
WrappedAsyncFunction(Box::new(move |rawlua, args| unsafe {
let lua = rawlua.lua();
let args = match A::from_lua_args(args, 1, None, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = func(lua, args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
let weak = rawlua.weak().clone();
Box::pin(async move { fut.await?.push_into_stack_multi(&weak.lock()) })
}))
}
}
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
impl IntoLua for WrappedFunction {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
lua.create_callback(self.0).map(Value::Function)
fn into_lua(self, lua: &Lua) -> Result<Value> {
lua.lock().create_callback(self.0).map(Value::Function)
}
}
#[cfg(feature = "async")]
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
impl IntoLua for WrappedAsyncFunction {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
lua.create_async_callback(self.0).map(Value::Function)
fn into_lua(self, lua: &Lua) -> Result<Value> {
lua.lock().create_async_callback(self.0).map(Value::Function)
}
}
@@ -649,8 +594,8 @@ impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Function: Send);
#[cfg(all(feature = "unstable", not(feature = "send")))]
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Function: Send, Sync);
}
+33 -21
View File
@@ -1,12 +1,14 @@
use std::borrow::Cow;
use std::cell::UnsafeCell;
use std::ops::Deref;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
use std::os::raw::c_int;
use ffi::lua_Debug;
use crate::lua::Lua;
use crate::state::RawLua;
use crate::types::ReentrantMutexGuard;
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
/// Contains information about currently executing Lua code.
@@ -18,25 +20,44 @@ use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
///
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
/// [`Lua::set_hook`]: crate::Lua::set_hook
pub struct Debug<'lua> {
lua: &'lua Lua,
pub struct Debug<'a> {
lua: EitherLua<'a>,
ar: ActivationRecord,
#[cfg(feature = "luau")]
level: c_int,
}
impl<'lua> Debug<'lua> {
enum EitherLua<'a> {
Owned(ReentrantMutexGuard<'a, RawLua>),
#[cfg(not(feature = "luau"))]
pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
Borrowed(&'a RawLua),
}
impl Deref for EitherLua<'_> {
type Target = RawLua;
fn deref(&self) -> &Self::Target {
match self {
EitherLua::Owned(guard) => guard,
#[cfg(not(feature = "luau"))]
EitherLua::Borrowed(lua) => lua,
}
}
}
impl<'a> Debug<'a> {
// We assume the lock is held when this function is called.
#[cfg(not(feature = "luau"))]
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
Debug {
lua,
lua: EitherLua::Borrowed(lua),
ar: ActivationRecord::Borrowed(ar),
}
}
pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
Debug {
lua,
lua: EitherLua::Owned(guard),
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
#[cfg(feature = "luau")]
level: _level,
@@ -234,7 +255,8 @@ pub struct DebugSource<'a> {
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
/// the main part of a chunk.
pub what: &'static str,
}
@@ -242,20 +264,10 @@ pub struct DebugSource<'a> {
pub struct DebugStack {
pub num_ups: i32,
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub num_params: i32,
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub is_vararg: bool,
}
+19 -33
View File
@@ -27,9 +27,9 @@
//!
//! # Serde support
//!
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua values
//! and vice versa using serde. Any user defined data type that implements [`serde::Serialize`] or
//! [`serde::Deserialize`] can be converted.
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua
//! values and vice versa using serde. Any user defined data type that implements
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
//!
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
@@ -40,14 +40,14 @@
//! # Async/await support
//!
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
//! [`AsyncThread`] using any runtime (eg. Tokio).
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or
//! polling [`AsyncThread`] using any runtime (eg. Tokio).
//!
//! Requires `feature = "async"`.
//!
//! # `Send` requirement
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds `Send` requirement
//! to [`Function`]s and [`UserData`].
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
//! `Send` requirement to [`Function`]s and [`UserData`].
//!
//! [Lua programming language]: https://www.lua.org/
//! [`Lua`]: crate::Lua
@@ -84,20 +84,18 @@ mod conversion;
mod error;
mod function;
mod hook;
mod lua;
#[cfg(feature = "luau")]
mod luau;
mod memory;
mod multi;
mod scope;
// mod scope;
mod state;
mod stdlib;
mod string;
mod table;
mod thread;
mod types;
mod userdata;
mod userdata_ext;
mod userdata_impl;
mod util;
mod value;
@@ -109,20 +107,18 @@ pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::lua::{GCMode, Lua, LuaOptions};
pub use crate::multi::Variadic;
pub use crate::scope::Scope;
pub use crate::state::{GCMode, Lua, LuaOptions};
// pub use crate::scope::Scope;
pub use crate::stdlib::StdLib;
pub use crate::string::String;
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
UserDataRef, UserDataRefMut,
AnyUserData, AnyUserDataExt, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
UserDataRef, UserDataRefMut, UserDataRegistry,
};
pub use crate::userdata_ext::AnyUserDataExt;
pub use crate::userdata_impl::UserDataRegistry;
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
#[cfg(not(feature = "luau"))]
@@ -141,9 +137,7 @@ pub use crate::thread::AsyncThread;
#[cfg(feature = "serialize")]
#[doc(inline)]
pub use crate::serde::{
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
};
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
@@ -154,13 +148,6 @@ pub mod serde;
#[macro_use]
extern crate mlua_derive;
// Unstable features
#[cfg(feature = "unstable")]
pub use crate::{
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
userdata::OwnedAnyUserData,
};
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
///
/// This macro allows to write Lua code directly in Rust code.
@@ -201,8 +188,8 @@ pub use crate::{
///
/// Other minor limitations:
///
/// - Certain escape codes in string literals don't work.
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
/// - Certain escape codes in string literals don't work. (Specifically: `\a`, `\b`, `\f`, `\v`,
/// `\123` (octal escape codes), `\u`, and `\U`).
///
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
///
@@ -266,7 +253,6 @@ pub use mlua_derive::FromLua;
/// ...
/// }
/// ```
///
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
@@ -279,6 +265,6 @@ pub(crate) mod private {
impl Sealed for Error {}
impl<T> Sealed for std::result::Result<T, Error> {}
impl Sealed for Lua {}
impl Sealed for Table<'_> {}
impl Sealed for AnyUserData<'_> {}
impl Sealed for Table {}
impl Sealed for AnyUserData {}
}
-3667
View File
File diff suppressed because it is too large Load Diff
+2 -5
View File
@@ -2,7 +2,7 @@ use std::ffi::CStr;
use std::os::raw::{c_float, c_int};
use crate::error::Result;
use crate::lua::Lua;
use crate::state::Lua;
// Since Luau has some missing standard functions, we re-implement them here
@@ -10,10 +10,7 @@ impl Lua {
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
let globals = self.globals();
globals.raw_set(
"collectgarbage",
self.create_c_function(lua_collectgarbage)?,
)?;
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
// Set `_VERSION` global to include version number
+8 -9
View File
@@ -7,7 +7,7 @@ use std::{env, fs};
use crate::chunk::ChunkMode;
use crate::error::Result;
use crate::lua::Lua;
use crate::state::Lua;
use crate::table::Table;
use crate::types::RegistryKey;
use crate::value::{IntoLua, Value};
@@ -53,7 +53,7 @@ impl std::ops::DerefMut for LoadedDylibs {
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
// Create the package table and store it in app_data for later use (bypassing globals lookup)
let package = lua.create_table()?;
lua.set_app_data(PackageKey(lua.create_registry_value(package.clone())?));
lua.set_app_data(PackageKey(lua.create_registry_value(&package)?));
// Set `package.path`
let mut search_path = env::var("LUAU_PATH")
@@ -82,12 +82,12 @@ pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
// Set `package.loaded` (table with a list of loaded modules)
let loaded = lua.create_table()?;
package.raw_set("loaded", loaded.clone())?;
package.raw_set("loaded", &loaded)?;
lua.set_named_registry_value("_LOADED", loaded)?;
// Set `package.loaders`
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
package.raw_set("loaders", loaders.clone())?;
package.raw_set("loaders", &loaders)?;
#[cfg(unix)]
{
loaders.push(lua.create_function(dylib_loader)?)?;
@@ -225,14 +225,13 @@ fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
let search_cpath = package.get::<_, StdString>("cpath").unwrap_or_default();
let find_symbol = |lib: &Library| unsafe {
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes())
{
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes()) {
return lua.create_c_function(*entry).map(Value::Function);
}
// Try all in one mode
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(
format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes(),
) {
if let Ok(entry) =
lib.get::<ffi::lua_CFunction>(format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes())
{
return lua.create_c_function(*entry).map(Value::Function);
}
"cannot find module entrypoint".into_lua(lua)
+1 -2
View File
@@ -10,8 +10,7 @@ macro_rules! bug_msg {
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
as *const ::std::os::raw::c_char
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
};
}
+57 -88
View File
@@ -1,135 +1,104 @@
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::result::Result as StdResult;
use crate::error::Result;
use crate::lua::Lua;
use crate::state::{Lua, RawLua};
use crate::util::check_stack;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
impl<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
match self {
Ok(v) => result.push_front(v.into_lua(lua)?),
Err(e) => {
result.push_front(e.into_lua(lua)?);
result.push_front(Nil);
}
Ok(val) => (val,).into_lua_multi(lua),
Err(err) => (Nil, err).into_lua_multi(lua),
}
Ok(result)
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
match self {
Ok(v) => v.push_into_stack(lua).map(|_| 1),
Err(e) => {
let state = lua.state();
check_stack(state, 3)?;
ffi::lua_pushnil(state);
e.push_into_stack(lua)?;
Ok(2)
}
Ok(val) => (val,).push_into_stack_multi(lua),
Err(err) => (Nil, err).push_into_stack_multi(lua),
}
}
}
impl<'lua, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<(), E> {
impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
match self {
Ok(_) => return Ok(MultiValue::new()),
Err(e) => {
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
result.push_front(e.into_lua(lua)?);
result.push_front(Nil);
Ok(result)
}
Ok(_) => Ok(MultiValue::new()),
Err(err) => (Nil, err).into_lua_multi(lua),
}
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
match self {
Ok(_) => Ok(0),
Err(e) => {
let state = lua.state();
check_stack(state, 3)?;
ffi::lua_pushnil(state);
e.push_into_stack(lua)?;
Ok(2)
}
Err(err) => (Nil, err).push_into_stack_multi(lua),
}
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
impl<T: IntoLua> IntoLuaMulti for T {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
v.push_front(self.into_lua(lua)?);
v.push_back(self.into_lua(lua)?);
Ok(v)
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
self.push_into_stack(lua)?;
Ok(1)
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
impl<T: FromLua> FromLuaMulti for T {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
}
#[inline]
fn from_lua_args(
mut args: MultiValue<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
fn from_lua_args(mut args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
}
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
if nvals == 0 {
return T::from_lua(Nil, lua);
return T::from_lua(Nil, lua.lua());
}
T::from_stack(-nvals, lua)
}
#[inline]
unsafe fn from_stack_args(
nargs: c_int,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
if nargs == 0 {
return T::from_lua_arg(Nil, i, to, lua);
return T::from_lua_arg(Nil, i, to, lua.lua());
}
T::from_stack_arg(-nargs, i, to, lua)
}
}
impl<'lua> IntoLuaMulti<'lua> for MultiValue<'lua> {
impl IntoLuaMulti for MultiValue {
#[inline]
fn into_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
Ok(self)
}
}
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
impl FromLuaMulti for MultiValue {
#[inline]
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
Ok(values)
}
}
@@ -205,21 +174,21 @@ impl<T> DerefMut for Variadic<T> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
values.extend_from_values(self.0.into_iter().map(|val| val.into_lua(lua)))?;
Ok(values)
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
impl<T: FromLua> FromLuaMulti for Variadic<T> {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
values
.drain_all()
.map(|e| T::from_lua(e, lua))
.drain(..)
.map(|val| T::from_lua(val, lua))
.collect::<Result<Vec<T>>>()
.map(Variadic)
}
@@ -227,26 +196,26 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
macro_rules! impl_tuple {
() => (
impl<'lua> IntoLuaMulti<'lua> for () {
impl IntoLuaMulti for () {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
Ok(MultiValue::with_lua_and_capacity(lua, 0))
}
#[inline]
unsafe fn push_into_stack_multi(self, _lua: &'lua Lua) -> Result<c_int> {
unsafe fn push_into_stack_multi(self, _lua: &RawLua) -> Result<c_int> {
Ok(0)
}
}
impl<'lua> FromLuaMulti<'lua> for () {
impl FromLuaMulti for () {
#[inline]
fn from_lua_multi(_values: MultiValue<'lua>, _lua: &'lua Lua) -> Result<Self> {
fn from_lua_multi(_values: MultiValue, _lua: &Lua) -> Result<Self> {
Ok(())
}
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
if nvals > 0 {
ffi::lua_pop(lua.state(), nvals);
}
@@ -256,13 +225,13 @@ macro_rules! impl_tuple {
);
($last:ident $($name:ident)*) => (
impl<'lua, $($name,)* $last> IntoLuaMulti<'lua> for ($($name,)* $last,)
where $($name: IntoLua<'lua>,)*
$last: IntoLuaMulti<'lua>
impl<$($name,)* $last> IntoLuaMulti for ($($name,)* $last,)
where $($name: IntoLua,)*
$last: IntoLuaMulti
{
#[allow(unused_mut, non_snake_case)]
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
let ($($name,)* $last,) = self;
let mut results = $last.into_lua_multi(lua)?;
@@ -272,7 +241,7 @@ macro_rules! impl_tuple {
#[allow(non_snake_case)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let ($($name,)* $last,) = self;
let mut nresults = 0;
$(
@@ -288,13 +257,13 @@ macro_rules! impl_tuple {
}
}
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
where $($name: FromLua<'lua>,)*
$last: FromLuaMulti<'lua>
impl<$($name,)* $last> FromLuaMulti for ($($name,)* $last,)
where $($name: FromLua,)*
$last: FromLuaMulti
{
#[allow(unused_mut, non_snake_case)]
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
Ok(($($name,)* $last,))
@@ -302,7 +271,7 @@ macro_rules! impl_tuple {
#[allow(unused_mut, non_snake_case)]
#[inline]
fn from_lua_args(mut args: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
fn from_lua_args(mut args: MultiValue, mut i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
$(
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
i += 1;
@@ -313,13 +282,13 @@ macro_rules! impl_tuple {
#[allow(unused_mut, non_snake_case)]
#[inline]
unsafe fn from_stack_multi(mut nvals: c_int, lua: &'lua Lua) -> Result<Self> {
unsafe fn from_stack_multi(mut nvals: c_int, lua: &RawLua) -> Result<Self> {
$(
let $name = if nvals > 0 {
nvals -= 1;
FromLua::from_stack(-(nvals + 1), lua)
} else {
FromLua::from_lua(Nil, lua)
FromLua::from_lua(Nil, lua.lua())
}?;
)*
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
@@ -328,13 +297,13 @@ macro_rules! impl_tuple {
#[allow(unused_mut, non_snake_case)]
#[inline]
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
$(
let $name = if nargs > 0 {
nargs -= 1;
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
} else {
FromLua::from_lua_arg(Nil, i, to, lua)
FromLua::from_lua_arg(Nil, i, to, lua.lua())
}?;
i += 1;
)*
+11 -21
View File
@@ -2,19 +2,17 @@
#[doc(no_inline)]
pub use crate::{
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk,
Error as LuaError, ErrorContext as LuaErrorContext, ExternalError as LuaExternalError,
ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, Function as LuaFunction,
FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua,
IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk, Error as LuaError,
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs,
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry,
Value as LuaValue,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
};
#[cfg(not(feature = "luau"))]
@@ -32,13 +30,5 @@ pub use crate::AsyncThread as LuaAsyncThread;
#[cfg(feature = "serialize")]
#[doc(no_inline)]
pub use crate::{
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
SerializeOptions as LuaSerializeOptions,
};
#[cfg(feature = "unstable")]
#[doc(no_inline)]
pub use crate::{
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable, OwnedThread as LuaOwnedThread,
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
};
+24 -24
View File
@@ -10,7 +10,7 @@ use serde::Serialize;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend, SubtypeId};
use crate::types::{Callback, CallbackUpvalue, MaybeSend, SubtypeId, ValueRef};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
@@ -38,11 +38,11 @@ where
'lua: 'scope,
{
lua: &'lua Lua,
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
destructors: RefCell<Vec<(ValueRef<'lua>, DestructorCallback<'lua>)>>,
_scope_invariant: PhantomData<Cell<&'scope ()>>,
}
type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
type DestructorCallback<'lua> = Box<dyn Fn(ValueRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
impl<'lua, 'scope> Scope<'lua, 'scope> {
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
@@ -63,7 +63,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: IntoLuaMulti<'callback>,
R: IntoLuaMulti,
F: Fn(&'callback Lua, A) -> Result<R> + 'scope,
{
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
@@ -97,7 +97,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: IntoLuaMulti<'callback>,
R: IntoLuaMulti,
F: FnMut(&'callback Lua, A) -> Result<R> + 'scope,
{
let func = RefCell::new(func);
@@ -656,7 +656,7 @@ impl<'lua, T> NonStaticUserDataRegistry<'lua, T> {
impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static,
V: IntoLua + Clone + 'static,
{
let name = name.as_ref().to_string();
self.fields.push((
@@ -670,7 +670,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
R: IntoLua,
{
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| unsafe {
method(lua, ud)?.push_into_stack_multi(lua)
@@ -694,7 +694,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
R: IntoLua,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
@@ -719,7 +719,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static,
V: IntoLua + Clone + 'static,
{
let name = name.as_ref().to_string();
let name2 = name.clone();
@@ -735,7 +735,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
where
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
R: IntoLua,
{
let name = name.as_ref().to_string();
let name2 = name.clone();
@@ -754,7 +754,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
@@ -768,7 +768,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
@@ -786,7 +786,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
@@ -801,7 +801,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
@@ -812,7 +812,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
@@ -826,7 +826,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
@@ -842,7 +842,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
@@ -853,7 +853,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
@@ -867,7 +867,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
@@ -885,7 +885,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
@@ -900,7 +900,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
@@ -911,7 +911,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
@@ -925,7 +925,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
@@ -941,7 +941,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: IntoLuaMulti,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
+37 -51
View File
@@ -14,8 +14,8 @@ use crate::value::Value;
/// A struct for deserializing Lua values into Rust values.
#[derive(Debug)]
pub struct Deserializer<'lua> {
value: Value<'lua>,
pub struct Deserializer {
value: Value,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
@@ -93,14 +93,14 @@ impl Options {
}
}
impl<'lua> Deserializer<'lua> {
impl Deserializer {
/// Creates a new Lua Deserializer for the `Value`.
pub fn new(value: Value<'lua>) -> Self {
pub fn new(value: Value) -> Self {
Self::new_with_options(value, Options::default())
}
/// Creates a new Lua Deserializer for the `Value` with custom options.
pub fn new_with_options(value: Value<'lua>, options: Options) -> Self {
pub fn new_with_options(value: Value, options: Options) -> Self {
Deserializer {
value,
options,
@@ -108,11 +108,7 @@ impl<'lua> Deserializer<'lua> {
}
}
fn from_parts(
value: Value<'lua>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
) -> Self {
fn from_parts(value: Value, options: Options, visited: Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
Deserializer {
value,
options,
@@ -121,7 +117,7 @@ impl<'lua> Deserializer<'lua> {
}
}
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
impl<'de> serde::Deserializer<'de> for Deserializer {
type Error = Error;
#[inline]
@@ -139,8 +135,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[cfg(feature = "luau")]
Value::Vector(_) => self.deserialize_seq(visitor),
Value::String(s) => match s.to_str() {
Ok(s) => visitor.visit_str(s),
Err(_) => visitor.visit_bytes(s.as_bytes()),
Ok(s) => visitor.visit_str(&s),
Err(_) => visitor.visit_bytes(&s.as_bytes()),
},
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
Value::Table(_) => self.deserialize_map(visitor),
@@ -150,8 +146,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let lua = ud.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
let buf = std::slice::from_raw_parts(buf as *const u8, size);
visitor.visit_bytes(buf)
@@ -266,10 +263,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
if deserializer.seq.count() == 0 {
Ok(seq)
} else {
Err(de::Error::invalid_length(
len,
&"fewer elements in the table",
))
Err(de::Error::invalid_length(len, &"fewer elements in the table"))
}
}
Value::UserData(ud) if ud.is_serializable() => {
@@ -291,12 +285,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
#[inline]
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value>
fn deserialize_tuple_struct<V>(self, _name: &'static str, _len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
@@ -313,7 +302,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
let _guard = RecursionGuard::new(&t, &self.visited);
let mut deserializer = MapDeserializer {
pairs: MapPairs::new(t, self.options.sort_keys)?,
pairs: MapPairs::new(&t, self.options.sort_keys)?,
value: None,
options: self.options,
visited: self.visited,
@@ -394,13 +383,13 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
}
struct SeqDeserializer<'lua> {
seq: TableSequence<'lua, Value<'lua>>,
struct SeqDeserializer<'a> {
seq: TableSequence<'a, Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
@@ -453,8 +442,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
Some(&n) => {
self.next += 1;
let visited = Rc::clone(&self.visited);
let deserializer =
Deserializer::from_parts(Value::Number(n as _), self.options, visited);
let deserializer = Deserializer::from_parts(Value::Number(n as _), self.options, visited);
seed.deserialize(deserializer).map(Some)
}
None => Ok(None),
@@ -466,13 +454,13 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
}
}
pub(crate) enum MapPairs<'lua> {
Iter(TablePairs<'lua, Value<'lua>, Value<'lua>>),
Vec(Vec<(Value<'lua>, Value<'lua>)>),
pub(crate) enum MapPairs<'a> {
Iter(TablePairs<'a, Value, Value>),
Vec(Vec<(Value, Value)>),
}
impl<'lua> MapPairs<'lua> {
pub(crate) fn new(t: Table<'lua>, sort_keys: bool) -> Result<Self> {
impl<'a> MapPairs<'a> {
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
if sort_keys {
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
@@ -497,8 +485,8 @@ impl<'lua> MapPairs<'lua> {
}
}
impl<'lua> Iterator for MapPairs<'lua> {
type Item = Result<(Value<'lua>, Value<'lua>)>;
impl Iterator for MapPairs<'_> {
type Item = Result<(Value, Value)>;
fn next(&mut self) -> Option<Self::Item> {
match self {
@@ -508,15 +496,15 @@ impl<'lua> Iterator for MapPairs<'lua> {
}
}
struct MapDeserializer<'lua> {
pairs: MapPairs<'lua>,
value: Option<Value<'lua>>,
struct MapDeserializer<'a> {
pairs: MapPairs<'a>,
value: Option<Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
processed: usize,
}
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
type Error = Error;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
@@ -566,16 +554,16 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
}
}
struct EnumDeserializer<'lua> {
struct EnumDeserializer {
variant: StdString,
value: Option<Value<'lua>>,
value: Option<Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
impl<'de> de::EnumAccess<'de> for EnumDeserializer {
type Error = Error;
type Variant = VariantDeserializer<'lua>;
type Variant = VariantDeserializer;
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant)>
where
@@ -591,13 +579,13 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
}
}
struct VariantDeserializer<'lua> {
value: Option<Value<'lua>>,
struct VariantDeserializer {
value: Option<Value>,
options: Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
impl<'de> de::VariantAccess<'de> for VariantDeserializer {
type Error = Error;
fn unit_variant(self) -> Result<()> {
@@ -615,9 +603,7 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
T: de::DeserializeSeed<'de>,
{
match self.value {
Some(value) => {
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
}
Some(value) => seed.deserialize(Deserializer::from_parts(value, self.options, self.visited)),
None => Err(de::Error::invalid_type(
de::Unexpected::UnitVariant,
&"newtype variant",
+11 -10
View File
@@ -2,11 +2,12 @@
use std::os::raw::c_void;
use serde::{de::DeserializeOwned, ser::Serialize};
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use crate::error::Result;
use crate::lua::Lua;
use crate::private::Sealed;
use crate::state::Lua;
use crate::table::Table;
use crate::util::check_stack;
use crate::value::Value;
@@ -99,7 +100,7 @@ pub trait LuaSerdeExt: Sealed {
/// "#).exec()
/// }
/// ```
fn to_value<'lua, T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
fn to_value<T: Serialize + ?Sized>(&self, t: &T) -> Result<Value>;
/// Converts `T` into a [`Value`] instance with options.
///
@@ -124,7 +125,7 @@ pub trait LuaSerdeExt: Sealed {
/// "#).exec()
/// }
/// ```
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
where
T: Serialize + ?Sized;
@@ -189,8 +190,7 @@ pub trait LuaSerdeExt: Sealed {
/// }
/// ```
#[allow(clippy::wrong_self_convention)]
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
-> Result<T>;
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options) -> Result<T>;
}
impl LuaSerdeExt for Lua {
@@ -199,20 +199,21 @@ impl LuaSerdeExt for Lua {
}
fn array_metatable(&self) -> Table {
let lua = self.lock();
unsafe {
push_array_metatable(self.ref_thread());
Table(self.pop_ref_thread())
push_array_metatable(lua.ref_thread());
Table(lua.pop_ref_thread())
}
}
fn to_value<'lua, T>(&'lua self, t: &T) -> Result<Value<'lua>>
fn to_value<T>(&self, t: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new(self))
}
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
where
T: Serialize + ?Sized,
{
+82 -89
View File
@@ -2,14 +2,14 @@ use serde::{ser, Serialize};
use super::LuaSerdeExt;
use crate::error::{Error, Result};
use crate::lua::Lua;
use crate::state::Lua;
use crate::table::Table;
use crate::value::{IntoLua, Value};
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
pub struct Serializer<'lua> {
lua: &'lua Lua,
pub struct Serializer<'a> {
lua: &'a Lua,
options: Options,
}
@@ -96,8 +96,8 @@ impl Options {
/// Sets [`detect_serde_json_arbitrary_precision`] option.
///
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua number.
/// Otherwise it will be serialized as an object (what serde does).
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua
/// number. Otherwise it will be serialized as an object (what serde does).
///
/// This option is disabled by default.
///
@@ -109,14 +109,14 @@ impl Options {
}
}
impl<'lua> Serializer<'lua> {
impl<'a> Serializer<'a> {
/// Creates a new Lua Serializer with default options.
pub fn new(lua: &'lua Lua) -> Self {
pub fn new(lua: &'a Lua) -> Self {
Self::new_with_options(lua, Options::default())
}
/// Creates a new Lua Serializer with custom options.
pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self {
pub fn new_with_options(lua: &'a Lua, options: Options) -> Self {
Serializer { lua, options }
}
}
@@ -124,28 +124,28 @@ impl<'lua> Serializer<'lua> {
macro_rules! lua_serialize_number {
($name:ident, $t:ty) => {
#[inline]
fn $name(self, value: $t) -> Result<Value<'lua>> {
fn $name(self, value: $t) -> Result<Value> {
value.into_lua(self.lua)
}
};
}
impl<'lua> ser::Serializer for Serializer<'lua> {
type Ok = Value<'lua>;
impl<'a> ser::Serializer for Serializer<'a> {
type Ok = Value;
type Error = Error;
// Associated types for keeping track of additional state while serializing
// compound data structures like sequences and maps.
type SerializeSeq = SerializeSeq<'lua>;
type SerializeTuple = SerializeSeq<'lua>;
type SerializeTupleStruct = SerializeSeq<'lua>;
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
type SerializeMap = SerializeMap<'lua>;
type SerializeStruct = SerializeStruct<'lua>;
type SerializeStructVariant = SerializeStructVariant<'lua>;
type SerializeSeq = SerializeSeq<'a>;
type SerializeTuple = SerializeSeq<'a>;
type SerializeTupleStruct = SerializeSeq<'a>;
type SerializeTupleVariant = SerializeTupleVariant<'a>;
type SerializeMap = SerializeMap<'a>;
type SerializeStruct = SerializeStruct<'a>;
type SerializeStructVariant = SerializeStructVariant<'a>;
#[inline]
fn serialize_bool(self, value: bool) -> Result<Value<'lua>> {
fn serialize_bool(self, value: bool) -> Result<Value> {
Ok(Value::Boolean(value))
}
@@ -164,22 +164,22 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
lua_serialize_number!(serialize_f64, f64);
#[inline]
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
fn serialize_char(self, value: char) -> Result<Value> {
self.serialize_str(&value.to_string())
}
#[inline]
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
fn serialize_str(self, value: &str) -> Result<Value> {
self.lua.create_string(value).map(Value::String)
}
#[inline]
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
fn serialize_bytes(self, value: &[u8]) -> Result<Value> {
self.lua.create_string(value).map(Value::String)
}
#[inline]
fn serialize_none(self) -> Result<Value<'lua>> {
fn serialize_none(self) -> Result<Value> {
if self.options.serialize_none_to_null {
Ok(self.lua.null())
} else {
@@ -188,7 +188,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
fn serialize_some<T>(self, value: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
@@ -196,7 +196,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_unit(self) -> Result<Value<'lua>> {
fn serialize_unit(self) -> Result<Value> {
if self.options.serialize_unit_to_null {
Ok(self.lua.null())
} else {
@@ -205,7 +205,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value> {
if self.options.serialize_unit_to_null {
Ok(self.lua.null())
} else {
@@ -219,12 +219,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Value<'lua>> {
) -> Result<Value> {
self.serialize_str(variant)
}
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
@@ -238,7 +238,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Value<'lua>>
) -> Result<Value>
where
T: Serialize + ?Sized,
{
@@ -255,7 +255,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
if self.options.set_array_metatable {
table.set_metatable(Some(self.lua.array_metatable()));
}
Ok(SerializeSeq::new(table, self.options))
Ok(SerializeSeq::new(self.lua, table, self.options))
}
#[inline]
@@ -264,11 +264,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[inline]
fn serialize_tuple_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
#[cfg(feature = "luau")]
if name == "Vector" && len == crate::types::Vector::SIZE {
return Ok(SerializeSeq::new_vector(self.lua, self.options));
@@ -286,6 +282,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
_len: usize,
) -> Result<Self::SerializeTupleVariant> {
Ok(SerializeTupleVariant {
lua: self.lua,
variant,
table: self.lua.create_table()?,
options: self.options,
@@ -295,6 +292,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
Ok(SerializeMap {
lua: self.lua,
key: None,
table: self.lua.create_table_with_capacity(0, len.unwrap_or(0))?,
options: self.options,
@@ -330,6 +328,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
len: usize,
) -> Result<Self::SerializeStructVariant> {
Ok(SerializeStructVariant {
lua: self.lua,
variant,
table: self.lua.create_table_with_capacity(0, len)?,
options: self.options,
@@ -338,19 +337,19 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
}
#[doc(hidden)]
pub struct SerializeSeq<'lua> {
lua: &'lua Lua,
pub struct SerializeSeq<'a> {
lua: &'a Lua,
#[cfg(feature = "luau")]
vector: Option<crate::types::Vector>,
table: Option<Table<'lua>>,
table: Option<Table>,
next: usize,
options: Options,
}
impl<'lua> SerializeSeq<'lua> {
const fn new(table: Table<'lua>, options: Options) -> Self {
impl<'a> SerializeSeq<'a> {
fn new(lua: &'a Lua, table: Table, options: Options) -> Self {
Self {
lua: table.0.lua,
lua,
#[cfg(feature = "luau")]
vector: None,
table: Some(table),
@@ -360,7 +359,7 @@ impl<'lua> SerializeSeq<'lua> {
}
#[cfg(feature = "luau")]
const fn new_vector(lua: &'lua Lua, options: Options) -> Self {
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
Self {
lua,
vector: Some(crate::types::Vector::zero()),
@@ -371,8 +370,8 @@ impl<'lua> SerializeSeq<'lua> {
}
}
impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeSeq for SerializeSeq<'_> {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
@@ -386,13 +385,13 @@ impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
Ok(Value::Table(self.table.unwrap()))
}
}
impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeTuple for SerializeSeq<'_> {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
@@ -402,13 +401,13 @@ impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
ser::SerializeSeq::end(self)
}
}
impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeTupleStruct for SerializeSeq<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
@@ -426,7 +425,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
#[cfg(feature = "luau")]
if let Some(vector) = self.vector {
return Ok(Value::Vector(vector));
@@ -436,49 +435,48 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
}
#[doc(hidden)]
pub struct SerializeTupleVariant<'lua> {
pub struct SerializeTupleVariant<'a> {
lua: &'a Lua,
variant: &'static str,
table: Table<'lua>,
table: Table,
options: Options,
}
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeTupleVariant for SerializeTupleVariant<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.table.raw_push(lua.to_value_with(value, self.options)?)
self.table.raw_push(self.lua.to_value_with(value, self.options)?)
}
fn end(self) -> Result<Value<'lua>> {
let lua = self.table.0.lua;
let table = lua.create_table()?;
fn end(self) -> Result<Value> {
let table = self.lua.create_table()?;
table.raw_set(self.variant, self.table)?;
Ok(Value::Table(table))
}
}
#[doc(hidden)]
pub struct SerializeMap<'lua> {
table: Table<'lua>,
key: Option<Value<'lua>>,
pub struct SerializeMap<'a> {
lua: &'a Lua,
table: Table,
key: Option<Value>,
options: Options,
}
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeMap for SerializeMap<'_> {
type Ok = Value;
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.key = Some(lua.to_value_with(key, self.options)?);
self.key = Some(self.lua.to_value_with(key, self.options)?);
Ok(())
}
@@ -486,29 +484,25 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let key = mlua_expect!(
self.key.take(),
"serialize_value called before serialize_key"
);
let value = lua.to_value_with(value, self.options)?;
let key = mlua_expect!(self.key.take(), "serialize_value called before serialize_key");
let value = self.lua.to_value_with(value, self.options)?;
self.table.raw_set(key, value)
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
Ok(Value::Table(self.table))
}
}
#[doc(hidden)]
pub struct SerializeStruct<'lua> {
lua: &'lua Lua,
inner: Option<Value<'lua>>,
pub struct SerializeStruct<'a> {
lua: &'a Lua,
inner: Option<Value>,
options: Options,
}
impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeStruct for SerializeStruct<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
@@ -529,7 +523,7 @@ impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
fn end(self) -> Result<Value> {
match self.inner {
Some(table @ Value::Table(_)) => Ok(table),
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
@@ -551,29 +545,28 @@ impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
}
#[doc(hidden)]
pub struct SerializeStructVariant<'lua> {
pub struct SerializeStructVariant<'a> {
lua: &'a Lua,
variant: &'static str,
table: Table<'lua>,
table: Table,
options: Options,
}
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
type Ok = Value<'lua>;
impl ser::SerializeStructVariant for SerializeStructVariant<'_> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.table
.raw_set(key, lua.to_value_with(value, self.options)?)?;
.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
Ok(())
}
fn end(self) -> Result<Value<'lua>> {
let lua = self.table.0.lua;
let table = lua.create_table_with_capacity(0, 1)?;
fn end(self) -> Result<Value> {
let table = self.lua.create_table_with_capacity(0, 1)?;
table.raw_set(self.variant, self.table)?;
Ok(Value::Table(table))
}
+1934
View File
File diff suppressed because it is too large Load Diff
+247
View File
@@ -0,0 +1,247 @@
use std::any::TypeId;
use std::cell::UnsafeCell;
use std::rc::Rc;
// use std::collections::VecDeque;
use std::mem::{self, MaybeUninit};
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::sync::Arc;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use crate::error::Result;
use crate::state::RawLua;
use crate::stdlib::StdLib;
use crate::types::{AppData, ReentrantMutex, XRc, XWeak};
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
#[cfg(any(feature = "luau", doc))]
use crate::chunk::Compiler;
#[cfg(feature = "async")]
use {futures_util::task::noop_waker_ref, std::ptr::NonNull, std::task::Waker};
use super::{Lua, WeakLua};
// Unique key to store `ExtraData` in the registry
static EXTRA_REGISTRY_KEY: u8 = 0;
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
// const MULTIVALUE_POOL_SIZE: usize = 64;
const REF_STACK_RESERVE: c_int = 1;
/// Data associated with the Lua state.
pub(crate) struct ExtraData {
// Same layout as `Lua`
pub(super) lua: MaybeUninit<XRc<ReentrantMutex<RawLua>>>,
// Same layout as `WeakLua`
pub(super) weak: MaybeUninit<XWeak<ReentrantMutex<RawLua>>>,
pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
// Container to store arbitrary data (extensions)
pub(super) app_data: AppData,
pub(super) safe: bool,
pub(super) libs: StdLib,
#[cfg(feature = "module")]
pub(super) skip_memory_check: bool,
// Auxiliary thread to store references
pub(super) ref_thread: *mut ffi::lua_State,
pub(super) ref_stack_size: c_int,
pub(super) ref_stack_top: c_int,
pub(super) ref_free: Vec<c_int>,
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
pub(super) wrapped_failure_pool: Vec<c_int>,
// Pool of `MultiValue` containers
// multivalue_pool: Vec<VecDeque<Value>>,
// Pool of `Thread`s (coroutines) for async execution
#[cfg(feature = "async")]
pub(super) thread_pool: Vec<c_int>,
// Address of `WrappedFailure` metatable
pub(super) wrapped_failure_mt_ptr: *const c_void,
// Waker for polling futures
#[cfg(feature = "async")]
pub(super) waker: NonNull<Waker>,
#[cfg(not(feature = "luau"))]
pub(super) hook_callback: Option<crate::types::HookCallback>,
#[cfg(not(feature = "luau"))]
pub(super) hook_thread: *mut ffi::lua_State,
#[cfg(feature = "lua54")]
pub(super) warn_callback: Option<crate::types::WarnCallback>,
#[cfg(feature = "luau")]
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
#[cfg(feature = "luau")]
pub(super) sandboxed: bool,
#[cfg(feature = "luau")]
pub(super) compiler: Option<Compiler>,
#[cfg(feature = "luau-jit")]
pub(super) enable_jit: bool,
}
impl Drop for ExtraData {
fn drop(&mut self) {
unsafe {
#[cfg(feature = "module")]
self.lua.assume_init_drop();
self.weak.assume_init_drop();
}
*self.registry_unref_list.lock() = None;
}
}
static EXTRA_TYPE_KEY: u8 = 0;
impl TypeKey for XRc<UnsafeCell<ExtraData>> {
#[inline(always)]
fn type_key() -> *const c_void {
&EXTRA_TYPE_KEY as *const u8 as *const c_void
}
}
impl ExtraData {
// Index of `error_traceback` function in auxiliary thread stack
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
pub(super) const ERROR_TRACEBACK_IDX: c_int = 1;
pub(super) unsafe fn init(state: *mut ffi::lua_State) -> XRc<UnsafeCell<Self>> {
// Create ref stack thread and place it in the registry to prevent it
// from being garbage collected.
let ref_thread = mlua_expect!(
protect_lua!(state, 0, 0, |state| {
let thread = ffi::lua_newthread(state);
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
thread
}),
"Error while creating ref thread",
);
let wrapped_failure_mt_ptr = {
get_internal_metatable::<WrappedFailure>(state);
let ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ptr
};
// Store `error_traceback` function on the ref stack
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
{
ffi::lua_pushcfunction(ref_thread, crate::util::error_traceback);
assert_eq!(ffi::lua_gettop(ref_thread), Self::ERROR_TRACEBACK_IDX);
}
let extra = XRc::new(UnsafeCell::new(ExtraData {
lua: MaybeUninit::uninit(),
weak: MaybeUninit::uninit(),
registered_userdata: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
app_data: AppData::default(),
safe: false,
libs: StdLib::NONE,
#[cfg(feature = "module")]
skip_memory_check: false,
ref_thread,
// We need some reserved stack space to move values in and out of the ref stack.
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
ref_stack_top: ffi::lua_gettop(ref_thread),
ref_free: Vec::new(),
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
// multivalue_pool: Vec::with_capacity(MULTIVALUE_POOL_SIZE),
#[cfg(feature = "async")]
thread_pool: Vec::new(),
wrapped_failure_mt_ptr,
#[cfg(feature = "async")]
waker: NonNull::from(noop_waker_ref()),
#[cfg(not(feature = "luau"))]
hook_callback: None,
#[cfg(not(feature = "luau"))]
hook_thread: ptr::null_mut(),
#[cfg(feature = "lua54")]
warn_callback: None,
#[cfg(feature = "luau")]
interrupt_callback: None,
#[cfg(feature = "luau")]
sandboxed: false,
#[cfg(feature = "luau")]
compiler: None,
#[cfg(feature = "luau-jit")]
enable_jit: true,
}));
// Store it in the registry
mlua_expect!(Self::store(&extra, state), "Error while storing extra data");
extra
}
pub(super) unsafe fn set_lua(&mut self, lua: &XRc<ReentrantMutex<RawLua>>) {
self.lua.write(XRc::clone(lua));
if cfg!(not(feature = "module")) {
XRc::decrement_strong_count(XRc::as_ptr(lua));
}
self.weak.write(XRc::downgrade(lua));
}
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
#[cfg(feature = "luau")]
if cfg!(not(feature = "module")) {
// In the main app we can use `lua_callbacks` to access ExtraData
return (*ffi::lua_callbacks(state)).userdata as *mut _;
}
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
// `ExtraData` can be null only when Lua state is foreign.
// This case in used in `Lua::try_from_ptr()`.
ffi::lua_pop(state, 1);
return ptr::null_mut();
}
let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Rc<UnsafeCell<ExtraData>>;
ffi::lua_pop(state, 1);
(*extra_ptr).get()
}
unsafe fn store(extra: &XRc<UnsafeCell<Self>>, state: *mut ffi::lua_State) -> Result<()> {
#[cfg(feature = "luau")]
if cfg!(not(feature = "module")) {
(*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _;
return Ok(());
}
push_internal_userdata(state, XRc::clone(extra), true)?;
protect_lua!(state, 1, 0, fn(state) {
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
})
}
#[inline(always)]
pub(super) unsafe fn lua(&self) -> &Lua {
mem::transmute(self.lua.assume_init_ref())
}
#[inline(always)]
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
&*self.lua.assume_init_ref().data_ptr()
}
#[inline(always)]
pub(super) unsafe fn weak(&self) -> &WeakLua {
mem::transmute(self.weak.assume_init_ref())
}
}
+1382
View File
File diff suppressed because it is too large Load Diff
+181
View File
@@ -0,0 +1,181 @@
use std::os::raw::c_int;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::ptr;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::state::{ExtraData, RawLua};
use crate::util::{self, get_internal_metatable, WrappedFailure};
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
// const MULTIVALUE_POOL_SIZE: usize = 64;
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
impl<'a> StateGuard<'a> {
pub(super) fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
state = inner.state.replace(state);
Self(inner, state)
}
}
impl<'a> Drop for StateGuard<'a> {
fn drop(&mut self) {
self.0.state.set(self.1);
}
}
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
// and instead reuses unsed values from previous calls (or allocates new).
pub(super) unsafe fn callback_error_ext<F, R>(
state: *mut ffi::lua_State,
mut extra: *mut ExtraData,
f: F,
) -> R
where
F: FnOnce(c_int) -> Result<R>,
{
if extra.is_null() {
extra = ExtraData::get(state);
}
let nargs = ffi::lua_gettop(state);
enum PreallocatedFailure {
New(*mut WrappedFailure),
Existing(i32),
}
impl PreallocatedFailure {
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
match (*extra).wrapped_failure_pool.pop() {
Some(index) => PreallocatedFailure::Existing(index),
None => {
// We need to check stack for Luau in case when callback is called from interrupt
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
// Place it to the beginning of the stack
let ud = WrappedFailure::new_userdata(state);
ffi::lua_insert(state, 1);
PreallocatedFailure::New(ud)
}
}
}
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
let ref_thread = (*extra).ref_thread;
match *self {
PreallocatedFailure::New(ud) => {
ffi::lua_settop(state, 1);
ud
}
PreallocatedFailure::Existing(index) => {
ffi::lua_settop(state, 0);
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
ffi::lua_pushvalue(ref_thread, index);
ffi::lua_xmove(ref_thread, state, 1);
ffi::lua_pushnil(ref_thread);
ffi::lua_replace(ref_thread, index);
(*extra).ref_free.push(index);
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
}
}
}
unsafe fn release(self, state: *mut ffi::lua_State, extra: *mut ExtraData) {
let ref_thread = (*extra).ref_thread;
match self {
PreallocatedFailure::New(_) => {
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
ffi::lua_rotate(state, 1, -1);
ffi::lua_xmove(state, ref_thread, 1);
let index = ref_stack_pop(extra);
(*extra).wrapped_failure_pool.push(index);
} else {
ffi::lua_remove(state, 1);
}
}
PreallocatedFailure::Existing(index) => {
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
(*extra).wrapped_failure_pool.push(index);
} else {
ffi::lua_pushnil(ref_thread);
ffi::lua_replace(ref_thread, index);
(*extra).ref_free.push(index);
}
}
}
}
}
// We cannot shadow Rust errors with Lua ones, so we need to reserve pre-allocated memory
// to store a wrapped failure (error or panic) *before* we proceed.
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
Ok(Ok(r)) => {
// Return unused `WrappedFailure` to the pool
prealloc_failure.release(state, extra);
r
}
Ok(Err(err)) => {
let wrapped_error = prealloc_failure.r#use(state, extra);
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
let traceback = util::to_string(state, -1);
ffi::lua_pop(state, 1);
traceback
} else {
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
ptr::write(
wrapped_error,
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
);
get_internal_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
Err(p) => {
let wrapped_panic = prealloc_failure.r#use(state, extra);
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
get_internal_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
}
}
pub(super) unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
let extra = &mut *extra;
if let Some(free) = extra.ref_free.pop() {
ffi::lua_replace(extra.ref_thread, free);
return free;
}
// Try to grow max stack size
if extra.ref_stack_top >= extra.ref_stack_size {
let mut inc = extra.ref_stack_size; // Try to double stack size
while inc > 0 && ffi::lua_checkstack(extra.ref_thread, inc) == 0 {
inc /= 2;
}
if inc == 0 {
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
// during unwinding.
ffi::lua_pop(extra.ref_thread, 1);
let top = extra.ref_stack_top;
// It is a user error to create enough references to exhaust the Lua max stack size for
// the ref thread.
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
}
extra.ref_stack_size += inc;
}
extra.ref_stack_top += 1;
extra.ref_stack_top
}
+1 -6
View File
@@ -8,12 +8,7 @@ impl StdLib {
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
///
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
pub const COROUTINE: StdLib = StdLib(1);
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
+159 -92
View File
@@ -1,8 +1,9 @@
use std::borrow::{Borrow, Cow};
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::os::raw::c_void;
use std::string::String as StdString;
use std::{fmt, slice, str};
use std::{cmp, fmt, slice, str};
#[cfg(feature = "serialize")]
use {
@@ -11,37 +12,17 @@ use {
};
use crate::error::{Error, Result};
use crate::types::LuaRef;
use crate::state::LuaGuard;
use crate::types::ValueRef;
/// Handle to an internal Lua string.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
#[derive(Clone)]
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
pub struct String(pub(crate) ValueRef);
/// Owned handle to an internal Lua string.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone)]
pub struct OwnedString(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedString {
/// Get borrowed handle to the underlying Lua string.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> String {
String(self.0.to_ref())
}
}
impl<'lua> String<'lua> {
/// Get a `&str` slice if the Lua string is valid UTF-8.
impl String {
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
///
/// # Examples
///
@@ -60,15 +41,17 @@ impl<'lua> String<'lua> {
/// # }
/// ```
#[inline]
pub fn to_str(&self) -> Result<&str> {
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
pub fn to_str(&self) -> Result<BorrowedStr> {
let BorrowedBytes(bytes, guard) = self.as_bytes();
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str",
message: Some(e.to_string()),
})
})?;
Ok(BorrowedStr(s, guard))
}
/// Converts this string to a [`Cow<str>`].
/// Converts this string to a [`StdString`].
///
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
@@ -87,8 +70,8 @@ impl<'lua> String<'lua> {
/// # }
/// ```
#[inline]
pub fn to_string_lossy(&self) -> Cow<'_, str> {
StdString::from_utf8_lossy(self.as_bytes())
pub fn to_string_lossy(&self) -> StdString {
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
}
/// Get the bytes that make up this string.
@@ -109,14 +92,20 @@ impl<'lua> String<'lua> {
/// # }
/// ```
#[inline]
pub fn as_bytes(&self) -> &[u8] {
let nulled = self.as_bytes_with_nul();
&nulled[..nulled.len() - 1]
pub fn as_bytes(&self) -> BorrowedBytes {
let (bytes, guard) = unsafe { self.to_slice() };
BorrowedBytes(&bytes[..bytes.len() - 1], guard)
}
/// Get the bytes that make up this string, including the trailing nul byte.
pub fn as_bytes_with_nul(&self) -> &[u8] {
let ref_thread = self.0.lua.ref_thread();
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
let (bytes, guard) = unsafe { self.to_slice() };
BorrowedBytes(bytes, guard)
}
unsafe fn to_slice(&self) -> (&[u8], LuaGuard) {
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
mlua_debug_assert!(
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
@@ -128,7 +117,7 @@ impl<'lua> String<'lua> {
// string type
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
slice::from_raw_parts(data as *const u8, size + 1)
(slice::from_raw_parts(data as *const u8, size + 1), lua)
}
}
@@ -141,21 +130,13 @@ impl<'lua> String<'lua> {
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedString {
OwnedString(self.0.into_owned())
}
}
impl<'lua> fmt::Debug for String<'lua> {
impl fmt::Debug for String {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.as_bytes();
// Check if the string is valid utf8
if let Ok(s) = str::from_utf8(bytes) {
if let Ok(s) = str::from_utf8(&bytes) {
return s.fmt(f);
}
@@ -180,27 +161,14 @@ impl<'lua> fmt::Debug for String<'lua> {
}
}
impl<'lua> AsRef<[u8]> for String<'lua> {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl<'lua> Borrow<[u8]> for String<'lua> {
fn borrow(&self) -> &[u8] {
self.as_bytes()
}
}
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
//
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str`, `String` and `mlua::String`
// itself.
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
//
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
// in other ways.
impl<'lua, T> PartialEq<T> for String<'lua>
impl<T> PartialEq<T> for String
where
T: AsRef<[u8]> + ?Sized,
{
@@ -209,55 +177,151 @@ where
}
}
impl<'lua> Eq for String<'lua> {}
impl PartialEq<String> for String {
fn eq(&self, other: &String) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl<'lua> Hash for String<'lua> {
impl PartialEq<&String> for String {
fn eq(&self, other: &&String) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl Eq for String {}
impl Hash for String {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for String<'lua> {
impl Serialize for String {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
match self.to_str() {
Ok(s) => serializer.serialize_str(s),
Err(_) => serializer.serialize_bytes(self.as_bytes()),
Ok(s) => serializer.serialize_str(&s),
Err(_) => serializer.serialize_bytes(&self.as_bytes()),
}
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedString {
/// Get a `&str` slice if the Lua string is valid UTF-8.
///
/// This is a shortcut for [`String::to_str()`].
#[inline]
pub fn to_str(&self) -> Result<&str> {
let s = self.to_ref();
// Reattach lifetime to &self
unsafe { std::mem::transmute(s.to_str()) }
}
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] LuaGuard);
/// Get the bytes that make up this string.
///
/// This is a shortcut for [`String::as_bytes()`].
#[inline]
pub fn as_bytes(&self) -> &[u8] {
let s = self.to_ref();
// Reattach lifetime to &self
unsafe { std::mem::transmute(s.as_bytes()) }
impl Deref for BorrowedStr<'_> {
type Target = str;
#[inline(always)]
fn deref(&self) -> &str {
self.0
}
}
#[cfg(feature = "unstable")]
impl fmt::Debug for OwnedString {
impl Borrow<str> for BorrowedStr<'_> {
#[inline(always)]
fn borrow(&self) -> &str {
self.0
}
}
impl AsRef<str> for BorrowedStr<'_> {
#[inline(always)]
fn as_ref(&self) -> &str {
self.0
}
}
impl fmt::Display for BorrowedStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_ref().fmt(f)
self.0.fmt(f)
}
}
impl fmt::Debug for BorrowedStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T> PartialEq<T> for BorrowedStr<'_>
where
T: AsRef<str>,
{
fn eq(&self, other: &T) -> bool {
self.0 == other.as_ref()
}
}
impl<T> PartialOrd<T> for BorrowedStr<'_>
where
T: AsRef<str>,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.0.partial_cmp(other.as_ref())
}
}
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] LuaGuard);
impl Deref for BorrowedBytes<'_> {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &[u8] {
self.0
}
}
impl Borrow<[u8]> for BorrowedBytes<'_> {
#[inline(always)]
fn borrow(&self) -> &[u8] {
self.0
}
}
impl AsRef<[u8]> for BorrowedBytes<'_> {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.0
}
}
impl fmt::Debug for BorrowedBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T> PartialEq<T> for BorrowedBytes<'_>
where
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
self.0 == other.as_ref()
}
}
impl<T> PartialOrd<T> for BorrowedBytes<'_>
where
T: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.0.partial_cmp(other.as_ref())
}
}
impl<'a> IntoIterator for BorrowedBytes<'a> {
type Item = &'a u8;
type IntoIter = slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
@@ -265,5 +329,8 @@ impl fmt::Debug for OwnedString {
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(String: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(String: Send, Sync);
}
+170 -225
View File
@@ -13,39 +13,19 @@ use {
use crate::error::{Error, Result};
use crate::function::Function;
use crate::private::Sealed;
use crate::types::{Integer, LuaRef};
use crate::state::{LuaGuard, RawLua};
use crate::types::{Integer, ValueRef};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
#[cfg(feature = "async")]
use futures_util::future::{self, LocalBoxFuture};
use std::future::Future;
/// Handle to an internal Lua table.
#[derive(Clone)]
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
pub struct Table(pub(crate) ValueRef);
/// Owned handle to an internal Lua table.
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedTable(pub(crate) crate::types::LuaOwnedRef);
#[cfg(feature = "unstable")]
impl OwnedTable {
/// Get borrowed handle to the underlying Lua table.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Table {
Table(self.0.to_ref())
}
}
impl<'lua> Table<'lua> {
impl Table {
/// Sets a key-value pair in the table.
///
/// If the value is `nil`, this will effectively remove the pair.
@@ -79,21 +59,21 @@ impl<'lua> Table<'lua> {
/// ```
///
/// [`raw_set`]: #method.raw_set
pub fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
pub fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
// Fast track
if !self.has_metatable() {
return self.raw_set(key, value);
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.0);
key.push_into_stack(lua)?;
value.push_into_stack(lua)?;
key.push_into_stack(&lua)?;
value.push_into_stack(&lua)?;
protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
}
}
@@ -122,50 +102,50 @@ impl<'lua> Table<'lua> {
/// ```
///
/// [`raw_get`]: #method.raw_get
pub fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
pub fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
// Fast track
if !self.has_metatable() {
return self.raw_get(key);
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_ref(&self.0);
key.push_into_stack(lua)?;
key.push_into_stack(&lua)?;
protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
V::from_stack(-1, lua)
V::from_stack(-1, &lua)
}
}
/// Checks whether the table contains a non-nil value for `key`.
///
/// This might invoke the `__index` metamethod.
pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
pub fn contains_key<K: IntoLua>(&self, key: K) -> Result<bool> {
Ok(self.get::<_, Value>(key)? != Value::Nil)
}
/// Appends a value to the back of the table.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
pub fn push<V: IntoLua>(&self, value: V) -> Result<()> {
// Fast track
if !self.has_metatable() {
return self.raw_push(value);
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_ref(&self.0);
value.push_into_stack(lua)?;
value.push_into_stack(&lua)?;
protect_lua!(state, 2, 0, fn(state) {
let len = ffi::luaL_len(state, -2) as Integer;
ffi::lua_seti(state, -2, len + 1);
@@ -177,13 +157,13 @@ impl<'lua> Table<'lua> {
/// Removes the last element from the table and returns it.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
pub fn pop<V: FromLua>(&self) -> Result<V> {
// Fast track
if !self.has_metatable() {
return self.raw_pop();
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -196,7 +176,7 @@ impl<'lua> Table<'lua> {
ffi::lua_pushnil(state);
ffi::lua_seti(state, -3, len);
})?;
V::from_stack(-1, lua)
V::from_stack(-1, &lua)
}
}
@@ -240,16 +220,12 @@ impl<'lua> Table<'lua> {
// If self does not define it, then check the other table.
if let Some(mt) = self.get_metatable() {
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
return mt.get::<_, Function>("__eq")?.call((self, other));
}
}
if let Some(mt) = other.get_metatable() {
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
return mt.get::<_, Function>("__eq")?.call((self, other));
}
}
@@ -257,19 +233,19 @@ impl<'lua> Table<'lua> {
}
/// Sets a key-value pair without invoking metamethods.
pub fn raw_set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
pub fn raw_set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.0);
key.push_into_stack(lua)?;
value.push_into_stack(lua)?;
key.push_into_stack(&lua)?;
value.push_into_stack(&lua)?;
if lua.unlikely_memory_error() {
ffi::lua_rawset(state, -3);
@@ -282,37 +258,37 @@ impl<'lua> Table<'lua> {
}
/// Gets the value associated to `key` without invoking metamethods.
pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua;
pub fn raw_get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_ref(&self.0);
key.push_into_stack(lua)?;
key.push_into_stack(&lua)?;
ffi::lua_rawget(state, -2);
V::from_stack(-1, lua)
V::from_stack(-1, &lua)
}
}
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
/// The worst case complexity is O(n), where n is the table length.
pub fn raw_insert<V: IntoLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
/// Inserts element value at position `idx` to the table, shifting up the elements from
/// `table[idx]`. The worst case complexity is O(n), where n is the table length.
pub fn raw_insert<V: IntoLua>(&self, idx: Integer, value: V) -> Result<()> {
let size = self.raw_len() as Integer;
if idx < 1 || idx > size + 1 {
return Err(Error::runtime("index out of bounds"));
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.0);
value.push_into_stack(lua)?;
value.push_into_stack(&lua)?;
protect_lua!(state, 2, 0, |state| {
for i in (idx..=size).rev() {
// table[i+1] = table[i]
@@ -325,18 +301,18 @@ impl<'lua> Table<'lua> {
}
/// Appends a value to the back of the table without invoking metamethods.
pub fn raw_push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
pub fn raw_push<V: IntoLua>(&self, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_ref(&self.0);
value.push_into_stack(lua)?;
value.push_into_stack(&lua)?;
unsafe fn callback(state: *mut ffi::lua_State) {
let len = ffi::lua_rawlen(state, -2) as Integer;
@@ -353,11 +329,11 @@ impl<'lua> Table<'lua> {
}
/// Removes the last element from the table and returns it, without invoking metamethods.
pub fn raw_pop<V: FromLua<'lua>>(&self) -> Result<V> {
pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -370,7 +346,7 @@ impl<'lua> Table<'lua> {
ffi::lua_pushnil(state);
ffi::lua_rawseti(state, -3, len);
V::from_stack(-1, lua)
V::from_stack(-1, &lua)
}
}
@@ -381,10 +357,10 @@ impl<'lua> Table<'lua> {
/// where n is the table length.
///
/// For other key types this is equivalent to setting `table[key] = nil`.
pub fn raw_remove<K: IntoLua<'lua>>(&self, key: K) -> Result<()> {
let lua = self.0.lua;
pub fn raw_remove<K: IntoLua>(&self, key: K) -> Result<()> {
let lua = self.0.lua.lock();
let state = lua.state();
let key = key.into_lua(lua)?;
let key = key.into_lua(lua.lua())?;
match key {
Value::Integer(idx) => {
let size = self.raw_len() as Integer;
@@ -418,7 +394,7 @@ impl<'lua> Table<'lua> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let lua = self.0.lua.lock();
unsafe {
#[cfg(feature = "luau")]
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
@@ -462,7 +438,7 @@ impl<'lua> Table<'lua> {
return Ok(self.raw_len() as Integer);
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -475,8 +451,8 @@ impl<'lua> Table<'lua> {
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
pub fn raw_len(&self) -> usize {
let ref_thread = self.0.lua.ref_thread();
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) }
let lua = self.0.lua.lock();
unsafe { ffi::lua_rawlen(lua.ref_thread(), self.0.index) }
}
/// Returns `true` if the table is empty, without invoking metamethods.
@@ -489,7 +465,7 @@ impl<'lua> Table<'lua> {
}
// Check hash part
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -508,8 +484,8 @@ impl<'lua> Table<'lua> {
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
///
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
pub fn get_metatable(&self) -> Option<Table<'lua>> {
let lua = self.0.lua;
pub fn get_metatable(&self) -> Option<Table> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -528,14 +504,14 @@ impl<'lua> Table<'lua> {
///
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
/// nothing).
pub fn set_metatable(&self, metatable: Option<Table<'lua>>) {
pub fn set_metatable(&self, metatable: Option<Table>) {
// Workaround to throw readonly error without returning Result
#[cfg(feature = "luau")]
if self.is_readonly() {
panic!("attempt to modify a readonly table");
}
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -555,7 +531,8 @@ impl<'lua> Table<'lua> {
#[doc(hidden)]
#[inline]
pub fn has_metatable(&self) -> bool {
let ref_thread = self.0.lua.ref_thread();
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
ffi::lua_pop(ref_thread, 1);
@@ -571,7 +548,8 @@ impl<'lua> Table<'lua> {
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_readonly(&self, enabled: bool) {
let ref_thread = self.0.lua.ref_thread();
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe {
ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
if !enabled {
@@ -587,7 +565,8 @@ impl<'lua> Table<'lua> {
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn is_readonly(&self) -> bool {
let ref_thread = self.0.lua.ref_thread();
let lua = self.0.lua.lock();
let ref_thread = lua.ref_thread();
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
}
@@ -602,26 +581,12 @@ impl<'lua> Table<'lua> {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedTable {
OwnedTable(self.0.into_owned())
}
/// Consume this table and return an iterator over the pairs of the table.
/// Returns an iterator over the pairs of the table.
///
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
///
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `K` and `V` types.
///
/// # Note
///
/// While this method consumes the `Table` object, it can not prevent code from mutating the
/// table while the iteration is in progress. Refer to the [Lua manual] for information about
/// the consequences of such mutation.
///
/// # Examples
///
/// Iterate over all globals:
@@ -643,9 +608,10 @@ impl<'lua> Table<'lua> {
///
/// [`Result`]: crate::Result
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
TablePairs {
table: self.0,
guard: self.0.lua.lock(),
table: self,
key: Some(Nil),
_phantom: PhantomData,
}
@@ -657,10 +623,10 @@ impl<'lua> Table<'lua> {
/// It does not invoke the `__pairs` metamethod.
pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
K: FromLua,
V: FromLua,
{
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -669,8 +635,8 @@ impl<'lua> Table<'lua> {
lua.push_ref(&self.0);
ffi::lua_pushnil(state);
while ffi::lua_next(state, -2) != 0 {
let k = K::from_stack(-2, lua)?;
let v = V::from_stack(-1, lua)?;
let k = K::from_stack(-2, &lua)?;
let v = V::from_stack(-1, &lua)?;
f(k, v)?;
// Keep key for next iteration
ffi::lua_pop(state, 1);
@@ -679,18 +645,12 @@ impl<'lua> Table<'lua> {
Ok(())
}
/// Consume this table and return an iterator over all values in the sequence part of the table.
/// Returns 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 behavior of Lua's `ipairs` function but does not invoke
/// any metamethods.
///
/// # Note
///
/// While this method consumes the `Table` object, it can not prevent code from mutating the
/// table while the iteration is in progress. Refer to the [Lua manual] for information about
/// the consequences of such mutation.
///
/// # Examples
///
/// ```
@@ -717,26 +677,21 @@ impl<'lua> Table<'lua> {
/// [`pairs`]: #method.pairs
/// [`Result`]: crate::Result
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
TableSequence {
table: self.0,
guard: self.0.lua.lock(),
table: self,
index: 1,
_phantom: PhantomData,
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "use `sequence_values` instead")]
pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
self.sequence_values()
}
#[cfg(feature = "serialize")]
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
where
V: FromLua<'lua>,
V: FromLua,
{
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -746,7 +701,7 @@ impl<'lua> Table<'lua> {
let len = ffi::lua_rawlen(state, -1);
for i in 1..=len {
ffi::lua_rawgeti(state, -1, i as _);
f(V::from_stack(-1, lua)?)?;
f(V::from_stack(-1, &lua)?)?;
ffi::lua_pop(state, 1);
}
}
@@ -755,18 +710,18 @@ impl<'lua> Table<'lua> {
/// Sets element value at position `idx` without invoking metamethods.
#[doc(hidden)]
pub fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
pub fn raw_seti<V: IntoLua>(&self, idx: usize, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.0);
value.push_into_stack(lua)?;
value.push_into_stack(&lua)?;
let idx = idx.try_into().unwrap();
if lua.unlikely_memory_error() {
@@ -780,7 +735,7 @@ impl<'lua> Table<'lua> {
#[cfg(feature = "serialize")]
pub(crate) fn is_array(&self) -> bool {
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -812,9 +767,8 @@ impl<'lua> Table<'lua> {
) -> fmt::Result {
visited.insert(self.to_pointer());
let t = self.clone();
// Collect key/value pairs into a vector so we can sort them
let mut pairs = t.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
// Sort keys
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
if pairs.is_empty() {
@@ -832,7 +786,7 @@ impl<'lua> Table<'lua> {
}
}
impl fmt::Debug for Table<'_> {
impl fmt::Debug for Table {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if fmt.alternate() {
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
@@ -841,25 +795,25 @@ impl fmt::Debug for Table<'_> {
}
}
impl<'lua> PartialEq for Table<'lua> {
impl PartialEq for Table {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
impl AsRef<Table> for Table {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl<'lua, T> PartialEq<[T]> for Table<'lua>
impl<T> PartialEq<[T]> for Table
where
T: IntoLua<'lua> + Clone,
T: IntoLua + Clone,
{
fn eq(&self, other: &[T]) -> bool {
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -874,7 +828,7 @@ where
if val == Nil {
return i == other.len();
}
match other.get(i).map(|v| v.clone().into_lua(lua)) {
match other.get(i).map(|v| v.clone().into_lua(lua.lua())) {
Some(Ok(other_val)) if val == other_val => continue,
_ => return false,
}
@@ -884,9 +838,9 @@ where
}
}
impl<'lua, T> PartialEq<&[T]> for Table<'lua>
impl<T> PartialEq<&[T]> for Table
where
T: IntoLua<'lua> + Clone,
T: IntoLua + Clone,
{
#[inline]
fn eq(&self, other: &&[T]) -> bool {
@@ -894,9 +848,9 @@ where
}
}
impl<'lua, T, const N: usize> PartialEq<[T; N]> for Table<'lua>
impl<T, const N: usize> PartialEq<[T; N]> for Table
where
T: IntoLua<'lua> + Clone,
T: IntoLua + Clone,
{
#[inline]
fn eq(&self, other: &[T; N]) -> bool {
@@ -905,24 +859,26 @@ where
}
/// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt<'lua>: Sealed {
pub trait TableExt: Sealed {
/// Calls the table as function assuming it has `__call` metamethod.
///
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
/// The metamethod is called with the table as its first argument, followed by the passed
/// arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Asynchronously calls the table as function assuming it has `__call` metamethod.
///
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
/// The metamethod is called with the table as its first argument, followed by the passed
/// arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and executes it,
/// passing the table itself along with `args` as function arguments.
@@ -933,8 +889,8 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
@@ -945,8 +901,8 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing the table itself along with `args` as function arguments and returning Future.
@@ -956,10 +912,10 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
@@ -969,98 +925,86 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
A: IntoLuaMulti,
R: FromLuaMulti;
}
impl<'lua> TableExt<'lua> for Table<'lua> {
impl TableExt for Table {
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
// Convert table to a function and call via pcall that respects the `__call` metamethod.
Function(self.0.clone()).call(args)
}
#[cfg(feature = "async")]
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
let func = Function(self.0.clone());
Box::pin(async move { func.call_async(args).await })
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
async move {
let func = Function(self.0.clone());
func.call_async(args?).await
}
}
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua;
let mut args = args.into_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(name)?.call(args)
self.get::<_, Function>(name)?.call((self, args))
}
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.get::<_, Function>(name)?.call(args)
}
#[cfg(feature = "async")]
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua;
let mut args = match args.into_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::Table(self.clone()));
self.call_async_function(name, args)
self.call_async_function(name, (self, args))
}
#[cfg(feature = "async")]
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let lua = self.0.lua;
let args = match args.into_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
match self.get::<_, Function>(name) {
Ok(func) => Box::pin(async move { func.call_async(args).await }),
Err(e) => Box::pin(future::err(e)),
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
async move {
let func = self.get::<_, Function>(name)?;
func.call_async(args?).await
}
}
}
/// A wrapped [`Table`] with customized serialization behavior.
#[cfg(feature = "serialize")]
pub(crate) struct SerializableTable<'a, 'lua> {
table: &'a Table<'lua>,
pub(crate) struct SerializableTable<'a> {
table: &'a Table,
options: crate::serde::de::Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for Table<'lua> {
impl Serialize for Table {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
@@ -1068,10 +1012,10 @@ impl<'lua> Serialize for Table<'lua> {
}
#[cfg(feature = "serialize")]
impl<'a, 'lua> SerializableTable<'a, 'lua> {
impl<'a> SerializableTable<'a> {
#[inline]
pub(crate) fn new(
table: &'a Table<'lua>,
table: &'a Table,
options: crate::serde::de::Options,
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
) -> Self {
@@ -1084,7 +1028,7 @@ impl<'a, 'lua> SerializableTable<'a, 'lua> {
}
#[cfg(feature = "serialize")]
impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
impl<'a> Serialize for SerializableTable<'a> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
@@ -1151,7 +1095,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
// Fast track
self.table.for_each(process_pair)
} else {
MapPairs::new(self.table.clone(), self.options.sort_keys)
MapPairs::new(self.table, self.options.sort_keys)
.map_err(serde::ser::Error::custom)?
.try_for_each(|kv| {
let (key, value) = kv?;
@@ -1168,30 +1112,31 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
/// This struct is created by the [`Table::pairs`] method.
///
/// [`Table::pairs`]: crate::Table::pairs
pub struct TablePairs<'lua, K, V> {
table: LuaRef<'lua>,
key: Option<Value<'lua>>,
pub struct TablePairs<'a, K, V> {
guard: LuaGuard,
table: &'a Table,
key: Option<Value>,
_phantom: PhantomData<(K, V)>,
}
impl<'lua, K, V> Iterator for TablePairs<'lua, K, V>
impl<'a, K, V> Iterator for TablePairs<'a, K, V>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
K: FromLua,
V: FromLua,
{
type Item = Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(prev_key) = self.key.take() {
let lua = self.table.lua;
let lua: &RawLua = &self.guard;
let state = lua.state();
let res = (|| unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_ref(&self.table);
lua.push_value(prev_key)?;
lua.push_ref(&self.table.0);
lua.push_value(&prev_key)?;
// It must be safe to call `lua_next` unprotected as deleting a key from a table is
// a permitted operation.
@@ -1200,8 +1145,8 @@ where
let key = lua.stack_value(-2);
Ok(Some((
key.clone(),
K::from_lua(key, lua)?,
V::from_stack(-1, lua)?,
K::from_lua(key, lua.lua())?,
V::from_stack(-1, &lua)?,
)))
} else {
Ok(None)
@@ -1227,21 +1172,21 @@ where
/// This struct is created by the [`Table::sequence_values`] method.
///
/// [`Table::sequence_values`]: crate::Table::sequence_values
pub struct TableSequence<'lua, V> {
// TODO: Use `&Table`
table: LuaRef<'lua>,
pub struct TableSequence<'a, V> {
guard: LuaGuard,
table: &'a Table,
index: Integer,
_phantom: PhantomData<V>,
}
impl<'lua, V> Iterator for TableSequence<'lua, V>
impl<'a, V> Iterator for TableSequence<'a, V>
where
V: FromLua<'lua>,
V: FromLua,
{
type Item = Result<V>;
fn next(&mut self) -> Option<Self::Item> {
let lua = self.table.lua;
let lua: &RawLua = &self.guard;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
@@ -1249,12 +1194,12 @@ where
return Some(Err(err));
}
lua.push_ref(&self.table);
lua.push_ref(&self.table.0);
match ffi::lua_rawgeti(state, -1, self.index) {
ffi::LUA_TNIL => None,
_ => {
self.index += 1;
Some(V::from_stack(-1, lua))
Some(V::from_stack(-1, &lua))
}
}
}
@@ -1265,8 +1210,8 @@ where
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Table: Send);
#[cfg(feature = "unstable")]
static_assertions::assert_not_impl_any!(OwnedTable: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Table: Send, Sync);
}
+73 -123
View File
@@ -2,8 +2,9 @@ use std::os::raw::{c_int, c_void};
use crate::error::{Error, Result};
#[allow(unused)]
use crate::lua::Lua;
use crate::types::LuaRef;
use crate::state::Lua;
use crate::state::RawLua;
use crate::types::ValueRef;
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
@@ -43,31 +44,12 @@ pub enum ThreadStatus {
/// Handle to an internal Lua thread (coroutine).
#[derive(Clone, Debug)]
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State);
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
/// Owned handle to an internal Lua thread (coroutine).
///
/// The owned handle holds a *strong* reference to the current Lua instance.
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
///
/// [`UserData`]: crate::UserData
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedThread(
pub(crate) crate::types::LuaOwnedRef,
pub(crate) *mut ffi::lua_State,
);
#[cfg(feature = "unstable")]
impl OwnedThread {
/// Get borrowed handle to the underlying Lua table.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> Thread {
Thread(self.0.to_ref(), self.1)
}
}
#[cfg(feature = "send")]
unsafe impl Send for Thread {}
#[cfg(feature = "send")]
unsafe impl Sync for Thread {}
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
///
@@ -78,20 +60,15 @@ impl OwnedThread {
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AsyncThread<'lua, R> {
thread: Thread<'lua>,
init_args: Option<Result<MultiValue<'lua>>>,
pub struct AsyncThread<R> {
thread: Thread,
init_args: Option<Result<MultiValue>>,
ret: PhantomData<R>,
recycle: bool,
}
impl<'lua> Thread<'lua> {
impl Thread {
#[inline(always)]
pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self {
let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) };
Thread(r#ref, state)
}
const fn state(&self) -> *mut ffi::lua_State {
self.1
}
@@ -139,14 +116,15 @@ impl<'lua> Thread<'lua> {
/// ```
pub fn resume<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
if self.status() != ThreadStatus::Resumable {
let lua = self.0.lua.lock();
if unsafe { self.status_unprotected() } != ThreadStatus::Resumable {
return Err(Error::CoroutineInactive);
}
let lua = self.0.lua;
let state = lua.state();
let thread_state = self.state();
unsafe {
@@ -157,19 +135,19 @@ impl<'lua> Thread<'lua> {
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
R::from_stack_multi(nresults, lua)
R::from_stack_multi(nresults, &lua)
}
}
/// Resumes execution of this thread.
///
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
unsafe fn resume_inner<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<c_int> {
let lua = self.0.lua;
unsafe fn resume_inner<A: IntoLuaMulti>(&self, args: A) -> Result<c_int> {
let lua = self.0.lua.lock();
let state = lua.state();
let thread_state = self.state();
let nargs = args.push_into_stack_multi(lua)?;
let nargs = args.push_into_stack_multi(&lua)?;
if nargs > 0 {
check_stack(thread_state, nargs)?;
ffi::lua_xmove(state, thread_state, nargs);
@@ -183,10 +161,7 @@ impl<'lua> Thread<'lua> {
return Err(pop_error(thread_state, ret));
}
check_stack(state, 3)?;
protect_lua!(state, 0, 1, |state| error_traceback_thread(
state,
thread_state
))?;
protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
return Err(pop_error(state, ret));
}
@@ -195,20 +170,25 @@ impl<'lua> Thread<'lua> {
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
let _guard = self.0.lua.lock();
unsafe { self.status_unprotected() }
}
/// Gets the status of the thread without locking the Lua state.
pub(crate) unsafe fn status_unprotected(&self) -> ThreadStatus {
let thread_state = self.state();
if thread_state == self.0.lua.state() {
// FIXME: skip double lock
if thread_state == self.0.lua.lock().state() {
// The coroutine is currently running
return ThreadStatus::Unresumable;
}
unsafe {
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
ThreadStatus::Error
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
ThreadStatus::Resumable
} else {
ThreadStatus::Unresumable
}
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
ThreadStatus::Error
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
ThreadStatus::Resumable
} else {
ThreadStatus::Unresumable
}
}
@@ -222,7 +202,7 @@ impl<'lua> Thread<'lua> {
where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
{
let lua = self.0.lua;
let lua = self.0.lua.lock();
unsafe {
lua.set_thread_hook(self.state(), triggers, callback);
}
@@ -244,8 +224,8 @@ impl<'lua> Thread<'lua> {
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
#[cfg(any(feature = "lua54", feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
let lua = self.0.lua;
pub fn reset(&self, func: crate::function::Function) -> Result<()> {
let lua = self.0.lua.lock();
let thread_state = self.state();
if thread_state == lua.state() {
return Err(Error::runtime("cannot reset a running thread"));
@@ -296,7 +276,7 @@ impl<'lua> Thread<'lua> {
///
/// ```
/// # use mlua::{Lua, Result, Thread};
/// use futures::stream::TryStreamExt;
/// use futures_util::stream::TryStreamExt;
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// # let lua = Lua::new();
@@ -323,12 +303,13 @@ impl<'lua> Thread<'lua> {
/// ```
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
pub fn into_async<A, R>(self, args: A) -> AsyncThread<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let args = args.into_lua_multi(self.0.lua);
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
AsyncThread {
thread: self,
init_args: Some(args),
@@ -372,7 +353,7 @@ impl<'lua> Thread<'lua> {
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
let lua = self.0.lua;
let lua = self.0.lua.lock();
let state = lua.state();
let thread_state = self.state();
unsafe {
@@ -391,44 +372,16 @@ impl<'lua> Thread<'lua> {
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedThread {
OwnedThread(self.0.into_owned(), self.1)
}
}
impl<'lua> PartialEq for Thread<'lua> {
impl PartialEq for Thread {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
// Additional shortcuts
#[cfg(feature = "unstable")]
impl OwnedThread {
/// Resumes execution of this thread.
///
/// See [`Thread::resume()`] for more details.
pub fn resume<'lua, A, R>(&'lua self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.to_ref().resume(args)
}
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
self.to_ref().status()
}
}
#[cfg(feature = "async")]
impl<'lua, R> AsyncThread<'lua, R> {
impl<R> AsyncThread<R> {
#[inline]
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
self.recycle = recyclable;
@@ -437,15 +390,15 @@ impl<'lua, R> AsyncThread<'lua, R> {
#[cfg(feature = "async")]
#[cfg(any(feature = "lua54", feature = "luau"))]
impl<'lua, R> Drop for AsyncThread<'lua, R> {
impl<R> Drop for AsyncThread<R> {
fn drop(&mut self) {
if self.recycle {
unsafe {
let lua = self.thread.0.lua;
let lua = self.thread.0.lua.lock();
// For Lua 5.4 this also closes all pending to-be-closed variables
if !lua.recycle_thread(&mut self.thread) {
#[cfg(feature = "lua54")]
if self.thread.status() == ThreadStatus::Error {
if self.thread.status_unprotected() == ThreadStatus::Error {
#[cfg(not(feature = "vendored"))]
ffi::lua_resetthread(self.thread.state());
#[cfg(feature = "vendored")]
@@ -458,24 +411,21 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
}
#[cfg(feature = "async")]
impl<'lua, R> Stream for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
impl<R: FromLuaMulti> Stream for AsyncThread<R> {
type Item = Result<R>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.thread.status() != ThreadStatus::Resumable {
return Poll::Ready(None);
}
let lua = self.thread.0.lua;
let lua = self.thread.0.lua.lock();
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
if self.thread.status_unprotected() != ThreadStatus::Resumable {
return Poll::Ready(None);
}
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(lua, cx.waker());
let _wg = WakerGuard::new(&lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
@@ -493,30 +443,27 @@ where
ffi::lua_xmove(thread_state, state, nresults);
cx.waker().wake_by_ref();
Poll::Ready(Some(R::from_stack_multi(nresults, lua)))
Poll::Ready(Some(R::from_stack_multi(nresults, &lua)))
}
}
}
#[cfg(feature = "async")]
impl<'lua, R> Future for AsyncThread<'lua, R>
where
R: FromLuaMulti<'lua>,
{
impl<R: FromLuaMulti> Future for AsyncThread<R> {
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.thread.status() != ThreadStatus::Resumable {
return Poll::Ready(Err(Error::CoroutineInactive));
}
let lua = self.thread.0.lua;
let lua = self.thread.0.lua.lock();
let state = lua.state();
let thread_state = self.thread.state();
unsafe {
if self.thread.status_unprotected() != ThreadStatus::Resumable {
return Poll::Ready(Err(Error::CoroutineInactive));
}
let _sg = StackGuard::new(state);
let _thread_sg = StackGuard::with_top(thread_state, 0);
let _wg = WakerGuard::new(lua, cx.waker());
let _wg = WakerGuard::new(&lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = self.get_unchecked_mut();
@@ -539,7 +486,7 @@ where
check_stack(state, nresults + 1)?;
ffi::lua_xmove(thread_state, state, nresults);
Poll::Ready(R::from_stack_multi(nresults, lua))
Poll::Ready(R::from_stack_multi(nresults, &lua))
}
}
}
@@ -552,7 +499,7 @@ unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
#[cfg(feature = "async")]
struct WakerGuard<'lua, 'a> {
lua: &'lua Lua,
lua: &'lua RawLua,
prev: NonNull<Waker>,
_phantom: PhantomData<&'a ()>,
}
@@ -560,7 +507,7 @@ struct WakerGuard<'lua, 'a> {
#[cfg(feature = "async")]
impl<'lua, 'a> WakerGuard<'lua, 'a> {
#[inline]
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
Ok(WakerGuard {
lua,
@@ -581,5 +528,8 @@ impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Thread: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Thread: Send, Sync);
}
+61 -250
View File
@@ -1,29 +1,27 @@
use std::any::{Any, TypeId};
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_int, c_void};
use std::result::Result as StdResult;
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::{Arc, Mutex};
use std::rc::Rc;
use std::sync::Arc;
use std::{fmt, mem, ptr};
use rustc_hash::FxHashMap;
use parking_lot::Mutex;
use crate::error::Result;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::lua::{ExtraData, Lua};
use crate::state::{ExtraData, Lua, RawLua, WeakLua};
#[cfg(feature = "async")]
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
#[cfg(feature = "unstable")]
use {crate::lua::LuaInner, std::marker::PhantomData};
#[cfg(all(feature = "luau", feature = "serialize"))]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
// Re-export mutex wrappers
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
/// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
@@ -43,21 +41,26 @@ pub(crate) enum SubtypeId {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> = Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int> + 'a>;
#[cfg(feature = "send")]
unsafe impl Send for LightUserData {}
#[cfg(feature = "send")]
unsafe impl Sync for LightUserData {}
pub(crate) type Callback<'a> = Box<dyn Fn(&'a RawLua, c_int) -> Result<c_int> + 'static>;
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: Arc<UnsafeCell<ExtraData>>,
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
}
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallback<'lua, 'a> =
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<c_int>> + 'a>;
pub(crate) type AsyncCallback<'a> =
Box<dyn Fn(&'a RawLua, MultiValue) -> LocalBoxFuture<'a, Result<c_int>> + 'static>;
#[cfg(feature = "async")]
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static>>;
#[cfg(feature = "async")]
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<c_int>>>;
@@ -71,16 +74,16 @@ pub enum VmState {
}
#[cfg(all(feature = "send", not(feature = "luau")))]
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()>>;
#[cfg(all(feature = "luau", feature = "send"))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
#[cfg(all(feature = "send", feature = "luau"))]
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
#[cfg(all(feature = "luau", not(feature = "send")))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
#[cfg(all(not(feature = "send"), feature = "luau"))]
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
@@ -165,7 +168,7 @@ impl Vector {
#[cfg(all(feature = "luau", feature = "serialize"))]
impl Serialize for Vector {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
ts.serialize_field(&self.x())?;
ts.serialize_field(&self.y())?;
@@ -190,7 +193,8 @@ pub(crate) struct DestructedUserdata;
///
/// This is a handle to a value stored inside the Lua registry. It is not automatically
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
/// and instances not manually removed can be garbage collected with
/// [`Lua::expire_registry_values`].
///
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
@@ -204,7 +208,7 @@ pub(crate) struct DestructedUserdata;
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
pub struct RegistryKey {
pub(crate) registry_id: AtomicI32,
pub(crate) registry_id: i32,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
}
@@ -233,7 +237,7 @@ impl Drop for RegistryKey {
let registry_id = self.id();
// We don't need to collect nil slot
if registry_id > ffi::LUA_REFNIL {
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
let mut unref_list = self.unref_list.lock();
if let Some(list) = unref_list.as_mut() {
list.push(registry_id);
}
@@ -245,7 +249,7 @@ impl RegistryKey {
/// Creates a new instance of `RegistryKey`
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
RegistryKey {
registry_id: AtomicI32::new(id),
registry_id: id,
unref_list,
}
}
@@ -253,13 +257,13 @@ impl RegistryKey {
/// Returns the underlying Lua reference of this `RegistryKey`
#[inline(always)]
pub fn id(&self) -> c_int {
self.registry_id.load(Ordering::Relaxed)
self.registry_id
}
/// Sets the unique Lua reference key of this `RegistryKey`
#[inline(always)]
pub(crate) fn set_id(&self, id: c_int) {
self.registry_id.store(id, Ordering::Relaxed);
pub(crate) fn set_id(&mut self, id: c_int) {
self.registry_id = id;
}
/// Destroys the `RegistryKey` without adding to the unref list
@@ -273,16 +277,17 @@ impl RegistryKey {
}
}
pub(crate) struct LuaRef<'lua> {
pub(crate) lua: &'lua Lua,
pub(crate) struct ValueRef {
pub(crate) lua: WeakLua,
pub(crate) index: c_int,
pub(crate) drop: bool,
}
impl<'lua> LuaRef<'lua> {
pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self {
LuaRef {
lua,
impl ValueRef {
#[inline]
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
ValueRef {
lua: lua.weak().clone(),
index,
drop: true,
}
@@ -290,249 +295,55 @@ impl<'lua> LuaRef<'lua> {
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
unsafe { ffi::lua_topointer(self.lua.ref_thread(), self.index) }
}
#[cfg(feature = "unstable")]
#[inline]
pub(crate) fn into_owned(self) -> LuaOwnedRef {
assert!(self.drop, "Cannot turn non-drop reference into owned");
let owned_ref = LuaOwnedRef::new(self.lua.clone(), self.index);
mem::forget(self);
owned_ref
let lua = self.lua.lock();
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
}
}
impl<'lua> fmt::Debug for LuaRef<'lua> {
impl fmt::Debug for ValueRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:p})", self.to_pointer())
}
}
impl<'lua> Clone for LuaRef<'lua> {
impl Clone for ValueRef {
fn clone(&self) -> Self {
self.lua.clone_ref(self)
unsafe { self.lua.lock().clone_ref(self) }
}
}
impl<'lua> Drop for LuaRef<'lua> {
impl Drop for ValueRef {
fn drop(&mut self) {
if self.drop {
self.lua.drop_ref_index(self.index);
if let Some(lua) = self.lua.try_lock() {
unsafe { lua.drop_ref(self) };
}
}
}
}
impl<'lua> PartialEq for LuaRef<'lua> {
impl PartialEq for ValueRef {
fn eq(&self, other: &Self) -> bool {
let ref_thread = self.lua.ref_thread();
assert!(
ref_thread == other.lua.ref_thread(),
self.lua == other.lua,
"Lua instance passed Value created from a different main Lua state"
);
unsafe { ffi::lua_rawequal(ref_thread, self.index, other.index) == 1 }
let lua = self.lua.lock();
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
}
}
#[cfg(feature = "unstable")]
pub(crate) struct LuaOwnedRef {
pub(crate) inner: Arc<LuaInner>,
pub(crate) index: c_int,
_non_send: PhantomData<*const ()>,
}
#[cfg(feature = "unstable")]
impl fmt::Debug for LuaOwnedRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OwnedRef({:p})", self.to_ref().to_pointer())
}
}
#[cfg(feature = "unstable")]
impl Clone for LuaOwnedRef {
fn clone(&self) -> Self {
self.to_ref().clone().into_owned()
}
}
#[cfg(feature = "unstable")]
impl Drop for LuaOwnedRef {
fn drop(&mut self) {
let lua: &Lua = unsafe { mem::transmute(&self.inner) };
lua.drop_ref_index(self.index);
}
}
#[cfg(feature = "unstable")]
impl LuaOwnedRef {
pub(crate) const fn new(inner: Arc<LuaInner>, index: c_int) -> Self {
LuaOwnedRef {
inner,
index,
_non_send: PhantomData,
}
}
pub(crate) const fn to_ref(&self) -> LuaRef {
LuaRef {
lua: unsafe { mem::transmute(&self.inner) },
index: self.index,
drop: false,
}
}
}
#[derive(Debug, Default)]
pub(crate) struct AppData {
#[cfg(not(feature = "send"))]
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>,
#[cfg(feature = "send")]
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>,
borrow: Cell<usize>,
}
impl AppData {
#[track_caller]
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
match self.try_insert(data) {
Ok(data) => data,
Err(_) => panic!("cannot mutably borrow app data container"),
}
}
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
if self.borrow.get() != 0 {
return Err(data);
}
// SAFETY: we checked that there are no other references to the container
Ok(unsafe { &mut *self.container.get() }
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
}
#[track_caller]
pub(crate) fn borrow<T: 'static>(&self) -> Option<AppDataRef<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRef {
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
borrow: &self.borrow,
})
}
#[track_caller]
pub(crate) fn borrow_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow_mut();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRefMut {
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
borrow: &self.borrow,
})
}
#[track_caller]
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
if self.borrow.get() != 0 {
panic!("cannot mutably borrow app data container");
}
// SAFETY: we checked that there are no other references to the container
unsafe { &mut *self.container.get() }
.remove(&TypeId::of::<T>())?
.into_inner()
.downcast::<T>()
.ok()
.map(|data| *data)
}
}
/// A wrapper type for an immutably borrowed value from an app data container.
///
/// This type is similar to [`Ref`].
pub struct AppDataRef<'a, T: ?Sized + 'a> {
data: Ref<'a, T>,
borrow: &'a Cell<usize>,
}
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
fn drop(&mut self) {
self.borrow.set(self.borrow.get() - 1);
}
}
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
/// A wrapper type for a mutably borrowed value from an app data container.
///
/// This type is similar to [`RefMut`].
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
data: RefMut<'a, T>,
borrow: &'a Cell<usize>,
}
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
fn drop(&mut self) {
self.borrow.set(self.borrow.get() - 1);
}
}
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
mod app_data;
mod sync;
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
static_assertions::assert_not_impl_any!(LuaRef: Send);
#[cfg(feature = "unstable")]
static_assertions::assert_not_impl_any!(LuaOwnedRef: Send);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(ValueRef: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(ValueRef: Send, Sync);
}
+176
View File
@@ -0,0 +1,176 @@
use std::any::{Any, TypeId};
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult;
use rustc_hash::FxHashMap;
use super::MaybeSend;
use crate::state::LuaGuard;
#[cfg(not(feature = "send"))]
type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>;
#[cfg(feature = "send")]
type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>;
/// A container for arbitrary data associated with the Lua state.
#[derive(Debug, Default)]
pub struct AppData {
container: Container,
borrow: Cell<usize>,
}
impl AppData {
#[track_caller]
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
match self.try_insert(data) {
Ok(data) => data,
Err(_) => panic!("cannot mutably borrow app data container"),
}
}
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
if self.borrow.get() != 0 {
return Err(data);
}
// SAFETY: we checked that there are no other references to the container
Ok(unsafe { &mut *self.container.get() }
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
}
#[track_caller]
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRef {
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
borrow: &self.borrow,
_guard: guard,
})
}
#[track_caller]
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
let data = unsafe { &*self.container.get() }
.get(&TypeId::of::<T>())?
.borrow_mut();
self.borrow.set(self.borrow.get() + 1);
Some(AppDataRefMut {
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
borrow: &self.borrow,
_guard: guard,
})
}
#[track_caller]
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
if self.borrow.get() != 0 {
panic!("cannot mutably borrow app data container");
}
// SAFETY: we checked that there are no other references to the container
unsafe { &mut *self.container.get() }
.remove(&TypeId::of::<T>())?
.into_inner()
.downcast::<T>()
.ok()
.map(|data| *data)
}
}
/// A wrapper type for an immutably borrowed value from an app data container.
///
/// This type is similar to [`Ref`].
pub struct AppDataRef<'a, T: ?Sized + 'a> {
data: Ref<'a, T>,
borrow: &'a Cell<usize>,
_guard: Option<LuaGuard>,
}
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
fn drop(&mut self) {
self.borrow.set(self.borrow.get() - 1);
}
}
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
/// A wrapper type for a mutably borrowed value from an app data container.
///
/// This type is similar to [`RefMut`].
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
data: RefMut<'a, T>,
borrow: &'a Cell<usize>,
_guard: Option<LuaGuard>,
}
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
fn drop(&mut self) {
self.borrow.set(self.borrow.get() - 1);
}
}
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(AppData: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(AppData: Send);
// Must be !Send
static_assertions::assert_not_impl_any!(AppDataRef<()>: Send);
static_assertions::assert_not_impl_any!(AppDataRefMut<()>: Send);
}
+77
View File
@@ -0,0 +1,77 @@
#[cfg(feature = "send")]
mod inner {
use parking_lot::{RawMutex, RawThreadId};
use std::sync::{Arc, Weak};
pub(crate) type XRc<T> = Arc<T>;
pub(crate) type XWeak<T> = Weak<T>;
pub(crate) type ReentrantMutex<T> = parking_lot::ReentrantMutex<T>;
pub(crate) type ReentrantMutexGuard<'a, T> = parking_lot::ReentrantMutexGuard<'a, T>;
pub(crate) type ArcReentrantMutexGuard<T> =
parking_lot::lock_api::ArcReentrantMutexGuard<RawMutex, RawThreadId, T>;
}
#[cfg(not(feature = "send"))]
mod inner {
use std::ops::Deref;
use std::rc::{Rc, Weak};
pub(crate) type XRc<T> = Rc<T>;
pub(crate) type XWeak<T> = Weak<T>;
pub(crate) struct ReentrantMutex<T>(T);
impl<T> ReentrantMutex<T> {
#[inline(always)]
pub(crate) fn new(val: T) -> Self {
ReentrantMutex(val)
}
#[inline(always)]
pub(crate) fn lock(&self) -> ReentrantMutexGuard<T> {
ReentrantMutexGuard(&self.0)
}
#[inline(always)]
pub(crate) fn lock_arc(self: &XRc<Self>) -> ArcReentrantMutexGuard<T> {
ArcReentrantMutexGuard(Rc::clone(self))
}
#[inline(always)]
pub(crate) fn into_lock_arc(self: XRc<Self>) -> ArcReentrantMutexGuard<T> {
ArcReentrantMutexGuard(self)
}
#[inline(always)]
pub(crate) fn data_ptr(&self) -> *const T {
&self.0 as *const _
}
}
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
impl<'a, T> Deref for ReentrantMutexGuard<'a, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.0
}
}
pub(crate) struct ArcReentrantMutexGuard<T>(XRc<ReentrantMutex<T>>);
impl<T> Deref for ArcReentrantMutexGuard<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0 .0
}
}
}
pub(crate) use inner::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
+194 -460
View File
File diff suppressed because it is too large Load Diff
+422
View File
@@ -0,0 +1,422 @@
use std::any::{type_name, TypeId};
use std::cell::{Cell, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::rc::Rc;
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, Serializer};
use crate::error::{Error, Result};
use crate::state::{Lua, LuaGuard, RawLua};
use crate::userdata::AnyUserData;
use crate::util::get_userdata;
use crate::value::{FromLua, Value};
// A enum for storing userdata values.
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
pub(crate) enum UserDataVariant<T> {
Default(Rc<InnerRefCell<T>>),
#[cfg(feature = "serialize")]
Serializable(Rc<InnerRefCell<Box<dyn erased_serde::Serialize>>>),
}
impl<T> Clone for UserDataVariant<T> {
#[inline]
fn clone(&self) -> Self {
match self {
Self::Default(inner) => Self::Default(Rc::clone(inner)),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => UserDataVariant::Serializable(Rc::clone(inner)),
}
}
}
impl<T> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Default(Rc::new(InnerRefCell::new(data)))
}
// Immutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) unsafe fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
UserDataBorrowRef::try_from(self)
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_make_ref(&self, guard: LuaGuard) -> Result<UserDataRef<T>> {
UserDataRef::try_from(self.clone(), guard)
}
// Mutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) unsafe fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
UserDataBorrowMut::try_from(self)
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_make_mut_ref(&self, guard: LuaGuard) -> Result<UserDataRefMut<T>> {
UserDataRefMut::try_from(self.clone(), guard)
}
// Returns the wrapped value.
//
// This method checks that we have exclusive access to the value.
pub(crate) fn into_inner(self) -> Result<T> {
set_writing(self.flag())?;
Ok(match self {
Self::Default(inner) => Rc::into_inner(inner).unwrap().value.into_inner(),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => unsafe {
let raw = Box::into_raw(Rc::into_inner(inner).unwrap().value.into_inner());
*Box::from_raw(raw as *mut T)
},
})
}
#[inline(always)]
fn flag(&self) -> &Cell<BorrowFlag> {
match self {
Self::Default(inner) => &inner.borrow,
#[cfg(feature = "serialize")]
Self::Serializable(inner) => &inner.borrow,
}
}
#[inline(always)]
fn as_ptr(&self) -> *mut T {
match self {
Self::Default(inner) => inner.value.get(),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
}
}
}
#[cfg(feature = "serialize")]
impl<T: Serialize + 'static> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self {
let data = Box::new(data) as Box<dyn erased_serde::Serialize>;
Self::Serializable(Rc::new(InnerRefCell::new(data)))
}
}
#[cfg(feature = "serialize")]
impl Serialize for UserDataVariant<()> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
UserDataVariant::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
UserDataVariant::Serializable(inner) => unsafe {
let _ = self.try_borrow().map_err(serde::ser::Error::custom)?;
(*inner.value.get()).serialize(serializer)
},
}
}
}
//
// Inspired by `std::cell::RefCell`` implementation
//
pub(crate) struct InnerRefCell<T> {
borrow: Cell<BorrowFlag>,
value: UnsafeCell<T>,
}
impl<T> InnerRefCell<T> {
#[inline(always)]
pub fn new(value: T) -> Self {
InnerRefCell {
borrow: Cell::new(UNUSED),
value: UnsafeCell::new(value),
}
}
}
/// A wrapper type for a [`UserData`] value that provides read access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRef<T> {
variant: UserDataVariant<T>,
#[allow(unused)]
guard: LuaGuard,
}
impl<T> Deref for UserDataRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.variant.as_ptr() }
}
}
impl<T> Drop for UserDataRef<T> {
#[inline]
fn drop(&mut self) {
unset_reading(self.variant.flag());
}
}
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T> UserDataRef<T> {
#[inline]
fn try_from(variant: UserDataVariant<T>, guard: LuaGuard) -> Result<Self> {
set_reading(variant.flag())?;
Ok(UserDataRef { variant, guard })
}
}
impl<T: 'static> FromLua for UserDataRef<T> {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
try_value_to_userdata::<T>(value)?.borrow()
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let guard = lua.lua().lock_arc();
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_make_ref(guard)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRefMut<T> {
variant: UserDataVariant<T>,
#[allow(unused)]
guard: LuaGuard,
}
impl<T> Deref for UserDataRefMut<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.variant.as_ptr() }
}
}
impl<T> DerefMut for UserDataRefMut<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.variant.as_ptr() }
}
}
impl<T> Drop for UserDataRefMut<T> {
#[inline]
fn drop(&mut self) {
unset_writing(self.variant.flag());
}
}
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T> UserDataRefMut<T> {
fn try_from(variant: UserDataVariant<T>, guard: LuaGuard) -> Result<Self> {
// There must currently be no existing references
set_writing(variant.flag())?;
Ok(UserDataRefMut { variant, guard })
}
}
impl<T: 'static> FromLua for UserDataRefMut<T> {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
try_value_to_userdata::<T>(value)?.borrow_mut()
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let guard = lua.lua().lock_arc();
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_make_mut_ref(guard)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
// Positive values represent the number of `Ref` active. Negative values
// represent the number of `RefMut` active. Multiple `RefMut`s can only be
// active at a time if they refer to distinct, nonoverlapping components of a
// `RefCell` (e.g., different ranges of a slice).
type BorrowFlag = isize;
const UNUSED: BorrowFlag = 0;
#[inline(always)]
fn is_writing(x: BorrowFlag) -> bool {
x < UNUSED
}
#[inline(always)]
fn is_reading(x: BorrowFlag) -> bool {
x > UNUSED
}
#[inline(always)]
fn set_writing(borrow: &Cell<BorrowFlag>) -> Result<()> {
let flag = borrow.get();
if flag != UNUSED {
return Err(Error::UserDataBorrowMutError);
}
borrow.set(UNUSED - 1);
Ok(())
}
#[inline(always)]
fn set_reading(borrow: &Cell<BorrowFlag>) -> Result<()> {
let flag = borrow.get().wrapping_add(1);
if !is_reading(flag) {
return Err(Error::UserDataBorrowError);
}
borrow.set(flag);
Ok(())
}
#[inline(always)]
#[track_caller]
fn unset_writing(borrow: &Cell<BorrowFlag>) {
let flag = borrow.get();
debug_assert!(is_writing(flag));
borrow.set(flag + 1);
}
#[inline(always)]
#[track_caller]
fn unset_reading(borrow: &Cell<BorrowFlag>) {
let flag = borrow.get();
debug_assert!(is_reading(flag));
borrow.set(flag - 1);
}
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
impl<'a, T> Drop for UserDataBorrowRef<'a, T> {
#[inline]
fn drop(&mut self) {
unset_reading(self.0.flag());
}
}
impl<'a, T> Deref for UserDataBorrowRef<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.0.as_ptr() }
}
}
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
type Error = Error;
#[inline(always)]
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
set_reading(variant.flag())?;
Ok(UserDataBorrowRef(variant))
}
}
impl<'a, T> UserDataBorrowRef<'a, T> {
#[inline(always)]
pub(crate) fn get_ref(&self) -> &'a T {
// SAFETY: `UserDataBorrowRef` is only created when the borrow flag is set to reading.
unsafe { &*self.0.as_ptr() }
}
}
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
impl<'a, T> Drop for UserDataBorrowMut<'a, T> {
#[inline]
fn drop(&mut self) {
unset_writing(self.0.flag());
}
}
impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.0.as_ptr() }
}
}
impl<'a, T> DerefMut for UserDataBorrowMut<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.as_ptr() }
}
}
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
type Error = Error;
#[inline(always)]
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
set_writing(variant.flag())?;
Ok(UserDataBorrowMut(variant))
}
}
impl<'a, T> UserDataBorrowMut<'a, T> {
#[inline(always)]
pub(crate) fn get_mut(&mut self) -> &'a mut T {
// SAFETY: `UserDataBorrowMut` is only created when the borrow flag is set to writing.
unsafe { &mut *self.0.as_ptr() }
}
}
#[inline]
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
match value {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
message: Some(format!("expected userdata of type {}", type_name::<T>())),
}),
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_all!(UserDataRef<()>: Sync, Send);
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Sync, Send);
}
+62 -72
View File
@@ -4,40 +4,42 @@ use crate::userdata::{AnyUserData, MetaMethod};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
#[cfg(feature = "async")]
use futures_util::future::{self, LocalBoxFuture};
use std::future::Future;
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
pub trait AnyUserDataExt<'lua>: Sealed {
pub trait AnyUserDataExt: Sealed {
/// Gets the value associated to `key` from the userdata, assuming it has `__index` metamethod.
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V>;
fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V>;
/// Sets the value associated to `key` in the userdata, assuming it has `__newindex` metamethod.
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()>;
fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()>;
/// Calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
/// The metamethod is called with the userdata as its first argument, followed by the passed
/// arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Asynchronously calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
/// The metamethod is called with the userdata as its first argument, followed by the passed
/// arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Calls the userdata method, assuming it has `__index` metamethod
/// and a function associated to `name`.
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing the table itself along with `args` as function arguments and returning Future.
@@ -47,10 +49,10 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
@@ -61,8 +63,8 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
A: IntoLuaMulti,
R: FromLuaMulti;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
@@ -72,90 +74,81 @@ pub trait AnyUserDataExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua;
A: IntoLuaMulti,
R: FromLuaMulti;
}
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
impl AnyUserDataExt for AnyUserData {
fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Index)? {
Value::Table(table) => table.raw_get(key),
Value::Function(func) => func.call((self.clone(), key)),
Value::Function(func) => func.call((self, key)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::NewIndex)? {
Value::Table(table) => table.raw_set(key, value),
Value::Function(func) => func.call((self.clone(), key, value)),
Value::Function(func) => func.call((self, key, value)),
_ => Err(Error::runtime("attempt to index a userdata value")),
}
}
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call((self.clone(), args)),
Value::Function(func) => func.call((self, args)),
_ => Err(Error::runtime("attempt to call a userdata value")),
}
}
#[cfg(feature = "async")]
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
let metatable = match self.get_metatable() {
Ok(metatable) => metatable,
Err(err) => return Box::pin(future::err(err)),
};
match metatable.get::<Value>(MetaMethod::Call) {
Ok(Value::Function(func)) => {
let mut args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
args.push_front(Value::UserData(self.clone()));
Box::pin(async move { func.call_async(args).await })
let lua = self.0.lua.lock();
let args = (self, args).into_lua_multi(lua.lua());
async move {
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call_async(args?).await,
_ => Err(Error::runtime("attempt to call a userdata value")),
}
Ok(_) => Box::pin(future::err(Error::runtime(
"attempt to call a userdata value",
))),
Err(err) => Box::pin(future::err(err)),
}
}
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.call_function(name, (self.clone(), args))
self.call_function(name, (self, args))
}
#[cfg(feature = "async")]
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
self.call_async_function(name, (self.clone(), args))
self.call_async_function(name, (self, args))
}
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
A: IntoLuaMulti,
R: FromLuaMulti,
{
match self.get(name)? {
Value::Function(func) => func.call(args),
@@ -167,24 +160,21 @@ impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
}
#[cfg(feature = "async")]
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'lua,
A: IntoLuaMulti,
R: FromLuaMulti,
{
match self.get(name) {
Ok(Value::Function(func)) => {
let args = match args.into_lua_multi(self.0.lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(async move { func.call_async(args).await })
let lua = self.0.lua.lock();
let args = args.into_lua_multi(lua.lua());
async move {
match self.get::<_, Value>(name)? {
Value::Function(func) => func.call_async(args?).await,
val => {
let msg = format!("attempt to call a {} value", val.type_name());
Err(Error::runtime(msg))
}
}
Ok(val) => {
let msg = format!("attempt to call a {} value", val.type_name());
Box::pin(future::err(Error::runtime(msg)))
}
Err(err) => Box::pin(future::err(err)),
}
}
}
+591
View File
@@ -0,0 +1,591 @@
#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)]
use std::any::TypeId;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::types::{Callback, MaybeSend};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use super::cell::{UserDataBorrowMut, UserDataBorrowRef, UserDataVariant};
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
std::future::{self, Future},
};
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<'a, T: 'static> {
// Fields
pub(crate) fields: Vec<(String, Callback<'a>)>,
pub(crate) field_getters: Vec<(String, Callback<'a>)>,
pub(crate) field_setters: Vec<(String, Callback<'a>)>,
pub(crate) meta_fields: Vec<(String, Callback<'a>)>,
// Methods
pub(crate) methods: Vec<(String, Callback<'a>)>,
#[cfg(feature = "async")]
pub(crate) async_methods: Vec<(String, AsyncCallback<'a>)>,
pub(crate) meta_methods: Vec<(String, Callback<'a>)>,
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'a>)>,
_type: PhantomData<T>,
}
impl<'a, T: 'static> UserDataRegistry<'a, T> {
pub(crate) const fn new() -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
methods: Vec::new(),
#[cfg(feature = "async")]
async_methods: Vec::new(),
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
_type: PhantomData,
}
}
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'a>
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
}
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
try_self_arg!(Err(err));
}
let state = rawlua.state();
// Find absolute "self" index before processing args
let index = ffi::lua_absindex(state, -nargs);
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(index)) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(borrow_userdata_ref::<T>(state, index));
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'a>
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
}
let method = RefCell::new(method);
Box::new(move |rawlua, nargs| unsafe {
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
try_self_arg!(Err(err));
}
let state = rawlua.state();
// Find absolute "self" index before processing args
let index = ffi::lua_absindex(state, -nargs);
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(index)) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(state, index));
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
#[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'a>
where
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
match $res {
Ok(res) => res,
Err(err) => return Box::pin(future::ready(Err(Error::bad_self_argument(&name, err)))),
}
};
}
Box::new(move |rawlua, mut args| unsafe {
let this = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let lua = rawlua.lua();
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
let args = A::from_lua_args(args, 2, Some(&name), lua);
let (ref_thread, index) = (rawlua.ref_thread(), this.0.index);
match try_self_arg!(this.type_id()) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(borrow_userdata_ref::<T>(ref_thread, index));
let args = match args {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = method(lua, ud.get_ref(), args);
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
}
_ => {
let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch);
Box::pin(future::ready(Err(err)))
}
}
})
}
#[cfg(feature = "async")]
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'a>
where
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
match $res {
Ok(res) => res,
Err(err) => return Box::pin(future::ready(Err(Error::bad_self_argument(&name, err)))),
}
};
}
Box::new(move |rawlua, mut args| unsafe {
let this = args
.pop_front()
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
let lua = rawlua.lua();
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
let args = A::from_lua_args(args, 2, Some(&name), lua);
let (ref_thread, index) = (rawlua.ref_thread(), this.0.index);
match try_self_arg!(this.type_id()) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(ref_thread, index));
let args = match args {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = method(lua, ud.get_mut(), args);
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
}
_ => {
let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch);
Box::pin(future::ready(Err(err)))
}
}
})
}
fn box_function<F, A, R>(name: &str, function: F) -> Callback<'a>
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
function(lua.lua(), args)?.push_into_stack_multi(lua)
})
}
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'a>
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
let function = RefCell::new(function);
Box::new(move |lua, nargs| unsafe {
let function = &mut *function
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
function(lua.lua(), args)?.push_into_stack_multi(lua)
})
}
#[cfg(feature = "async")]
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'a>
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = get_function_name::<T>(name);
Box::new(move |rawlua, args| unsafe {
let lua = rawlua.lua();
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::ready(Err(e))),
};
let fut = function(lua, args);
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
})
}
pub(crate) fn check_meta_field<V>(lua: &Lua, name: &str, value: V) -> Result<Value>
where
V: IntoLua,
{
let value = value.into_lua(lua)?;
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
match value {
Value::Nil | Value::Table(_) | Value::Function(_) => {}
_ => {
return Err(Error::MetaMethodTypeError {
method: name.to_string(),
type_name: value.type_name(),
message: Some("expected nil, table or function".to_string()),
})
}
}
}
value.into_lua(lua)
}
}
// Returns function name for the type `T`, without the module path
fn get_function_name<T>(name: &str) -> StdString {
format!("{}.{name}", short_type_name::<T>())
}
impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
fn add_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + Clone + 'static,
{
let name = name.to_string();
let callback = Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) });
self.fields.push((name, callback));
}
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_method(&name, move |lua, data, ()| method(lua, data));
self.field_getters.push((name, callback));
}
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
self.field_setters.push((name, callback));
}
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
self.field_getters.push((name, callback));
}
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
where
F: FnMut(&'a Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
self.field_setters.push((name, callback));
}
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + Clone + 'static,
{
let name = name.to_string();
self.meta_fields.push((
name.clone(),
Box::new(move |lua, _| unsafe {
Self::check_meta_field(lua.lua(), &name, value.clone())?.push_into_stack_multi(lua)
}),
));
}
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
where
F: Fn(&'a Lua) -> Result<R> + MaybeSend + 'static,
R: IntoLua,
{
let name = name.to_string();
self.meta_fields.push((
name.clone(),
Box::new(move |rawlua, _| unsafe {
let lua = rawlua.lua();
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack_multi(rawlua)
}),
));
}
}
impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
self.methods.push((name, callback));
}
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
self.async_methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
self.async_methods.push((name, callback));
}
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
self.methods.push((name, callback));
}
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
self.async_methods.push((name, callback));
}
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
self.meta_methods.push((name, callback));
}
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
self.meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
self.async_meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
self.async_meta_methods.push((name, callback));
}
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
self.meta_methods.push((name, callback));
}
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
where
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
self.meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
where
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
FR: Future<Output = Result<R>> + 'a,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
self.async_meta_methods.push((name, callback));
}
}
// Borrow the userdata in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_ref<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowRef<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow()
}
// Borrow the userdata mutably in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_mut<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowMut<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
fn register(registry: &mut UserDataRegistry<Self>) {
let mut orig_registry = UserDataRegistry::new();
T::register(&mut orig_registry);
// Copy all fields, methods, etc. from the original registry
registry.fields.extend(orig_registry.fields);
registry.field_getters.extend(orig_registry.field_getters);
registry.field_setters.extend(orig_registry.field_setters);
registry.meta_fields.extend(orig_registry.meta_fields);
registry.methods.extend(orig_registry.methods);
#[cfg(feature = "async")]
registry.async_methods.extend(orig_registry.async_methods);
registry.meta_methods.extend(orig_registry.meta_methods);
#[cfg(feature = "async")]
registry
.async_meta_methods
.extend(orig_registry.async_meta_methods);
}
}
};
}
// A special proxy object for UserData
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
lua_userdata_impl!(UserDataProxy<T>);
-797
View File
@@ -1,797 +0,0 @@
#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)]
use std::any::TypeId;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::string::String as StdString;
use std::sync::{Arc, Mutex, RwLock};
use crate::error::{Error, Result};
use crate::lua::Lua;
use crate::types::{Callback, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
#[cfg(not(feature = "send"))]
use std::rc::Rc;
#[cfg(feature = "async")]
use {crate::types::AsyncCallback, futures_util::future, std::future::Future};
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<'lua, T: 'static> {
// Fields
pub(crate) fields: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) meta_fields: Vec<(String, Callback<'lua, 'static>)>,
// Methods
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
pub(crate) async_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
pub(crate) meta_methods: Vec<(String, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
_type: PhantomData<T>,
}
impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
pub(crate) const fn new() -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
methods: Vec::new(),
#[cfg(feature = "async")]
async_methods: Vec::new(),
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
_type: PhantomData,
}
}
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::new(move |lua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
try_self_arg!(Err(err));
}
let state = lua.state();
// Find absolute "self" index before processing args
let index = ffi::lua_absindex(state, -nargs);
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
match try_self_arg!(lua.get_userdata_type_id(index)) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(get_userdata_ref::<T>(state, index));
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => {
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(state, index));
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state, index));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<T>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(state, index));
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state, index));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state, index));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
method(lua, &ud, args?)?.push_into_stack_multi(lua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
let method = RefCell::new(method);
Box::new(move |lua, nargs| unsafe {
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
try_self_arg!(Err(err));
}
let state = lua.state();
// Find absolute "self" index before processing args
let index = ffi::lua_absindex(state, -nargs);
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
match try_self_arg!(lua.get_userdata_type_id(index)) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(get_userdata_mut::<T>(state, index));
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state, index));
let mut ud = try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state, index));
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state, index);
let ud = try_self_arg!(ud);
let mut ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state, index));
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state, index);
let ud = try_self_arg!(ud);
let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
#[cfg(feature = "async")]
fn box_async_method<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = Arc::new(get_function_name::<T>(name));
let method = Arc::new(method);
Box::new(move |lua, mut args| unsafe {
let name = name.clone();
let method = method.clone();
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::pin(async move {
let this = args.pop_front().ok_or_else(|| {
Error::from_lua_conversion("missing argument", "userdata", None)
});
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
let args = A::from_lua_args(args, 2, Some(&name), lua);
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
match try_self_arg!(this.type_id()) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<T>>() => {
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud =
try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<T>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud =
try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud =
try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
let ud = std::mem::transmute::<&T, &T>(&ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
})
}
#[cfg(feature = "async")]
fn box_async_method_mut<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = Arc::new(get_function_name::<T>(name));
let method = Arc::new(method);
Box::new(move |lua, mut args| unsafe {
let name = name.clone();
let method = method.clone();
macro_rules! try_self_arg {
($res:expr) => {
$res.map_err(|err| Error::bad_self_argument(&name, err))?
};
($res:expr, $err:expr) => {
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
};
}
Box::pin(async move {
let this = args.pop_front().ok_or_else(|| {
Error::from_lua_conversion("missing argument", "userdata", None)
});
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
let args = A::from_lua_args(args, 2, Some(&name), lua);
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
match try_self_arg!(this.type_id()) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
Err(Error::UserDataBorrowMutError)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud =
try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
let mut ud =
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud =
try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud =
try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
method(lua, ud, args?).await?.push_into_stack_multi(lua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
})
}
fn box_function<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
function(lua, args)?.push_into_stack_multi(lua)
})
}
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
let function = RefCell::new(function);
Box::new(move |lua, nargs| unsafe {
let function = &mut *function
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
function(lua, args)?.push_into_stack_multi(lua)
})
}
#[cfg(feature = "async")]
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'lua, 'static>
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
{
let name = get_function_name::<T>(name);
Box::new(move |lua, args| unsafe {
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
let fut = function(lua, args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
})
}
pub(crate) fn check_meta_field<V>(lua: &'lua Lua, name: &str, value: V) -> Result<Value<'lua>>
where
V: IntoLua<'lua>,
{
let value = value.into_lua(lua)?;
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
match value {
Value::Nil | Value::Table(_) | Value::Function(_) => {}
_ => {
return Err(Error::MetaMethodTypeError {
method: name.to_string(),
type_name: value.type_name(),
message: Some("expected nil, table or function".to_string()),
})
}
}
}
value.into_lua(lua)
}
}
// Returns function name for the type `T`, without the module path
fn get_function_name<T>(name: &str) -> StdString {
format!("{}.{name}", short_type_name::<T>())
}
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static,
{
let name = name.as_ref().to_string();
self.fields.push((
name,
Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) }),
));
}
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
{
let name = name.as_ref();
let method = Self::box_method(name, move |lua, data, ()| method(lua, data));
self.field_getters.push((name.into(), method));
}
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
A: FromLua<'lua>,
{
let name = name.as_ref();
let method = Self::box_method_mut(name, method);
self.field_setters.push((name.into(), method));
}
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
{
let name = name.as_ref();
let func = Self::box_function(name, function);
self.field_getters.push((name.into(), func));
}
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
where
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
A: FromLua<'lua>,
{
let name = name.as_ref();
let func = Self::box_function_mut(name, move |lua, (data, val)| function(lua, data, val));
self.field_setters.push((name.into(), func));
}
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static,
{
let name = name.as_ref().to_string();
let name2 = name.clone();
self.meta_fields.push((
name,
Box::new(move |lua, _| unsafe {
Self::check_meta_field(lua, &name2, value.clone())?.push_into_stack_multi(lua)
}),
));
}
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
where
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
{
let name = name.as_ref().to_string();
let name2 = name.clone();
self.meta_fields.push((
name,
Box::new(move |lua, _| unsafe {
Self::check_meta_field(lua, &name2, f(lua)?)?.push_into_stack_multi(lua)
}),
));
}
// Below are internal methods
fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
self.fields.extend(other.fields);
self.field_getters.extend(other.field_getters);
self.field_setters.extend(other.field_setters);
self.meta_fields.extend(other.meta_fields);
}
}
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistry<'lua, T> {
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.methods
.push((name.into(), Self::box_method(name, method)));
}
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.methods
.push((name.into(), Self::box_method_mut(name, method)));
}
#[cfg(feature = "async")]
fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_methods
.push((name.into(), Self::box_async_method(name, method)));
}
#[cfg(feature = "async")]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_methods
.push((name.into(), Self::box_async_method_mut(name, method)));
}
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.methods
.push((name.into(), Self::box_function(name, function)));
}
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.methods
.push((name.into(), Self::box_function_mut(name, function)));
}
#[cfg(feature = "async")]
fn add_async_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_methods
.push((name.into(), Self::box_async_function(name, function)));
}
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.meta_methods
.push((name.into(), Self::box_method(name, method)));
}
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.meta_methods
.push((name.into(), Self::box_method_mut(name, method)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_meta_methods
.push((name.into(), Self::box_async_method(name, method)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_meta_methods
.push((name.into(), Self::box_async_method_mut(name, method)));
}
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.meta_methods
.push((name.into(), Self::box_function(name, function)));
}
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.meta_methods
.push((name.into(), Self::box_function_mut(name, function)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_meta_methods
.push((name.into(), Self::box_async_function(name, function)));
}
// Below are internal methods used in generated code
fn append_methods_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
self.methods.extend(other.methods);
#[cfg(feature = "async")]
self.async_methods.extend(other.async_methods);
self.meta_methods.extend(other.meta_methods);
#[cfg(feature = "async")]
self.async_meta_methods.extend(other.async_meta_methods);
}
}
#[inline]
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State, index: c_int) -> Result<Ref<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow()
}
#[inline]
unsafe fn get_userdata_mut<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<RefMut<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
let mut orig_fields = UserDataRegistry::new();
T::add_fields(&mut orig_fields);
fields.append_fields_from(orig_fields);
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
let mut orig_methods = UserDataRegistry::new();
T::add_methods(&mut orig_methods);
methods.append_methods_from(orig_methods);
}
}
};
}
#[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<T>);
#[cfg(not(feature = "send"))]
lua_userdata_impl!(Rc<RefCell<T>>);
lua_userdata_impl!(Arc<T>);
lua_userdata_impl!(Arc<Mutex<T>>);
lua_userdata_impl!(Arc<RwLock<T>>);
#[cfg(feature = "parking_lot")]
lua_userdata_impl!(Arc<parking_lot::Mutex<T>>);
#[cfg(feature = "parking_lot")]
lua_userdata_impl!(Arc<parking_lot::RwLock<T>>);
// A special proxy object for UserData
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
lua_userdata_impl!(UserDataProxy<T>);
+432
View File
@@ -0,0 +1,432 @@
use std::any::Any;
use std::fmt::Write as _;
use std::mem::MaybeUninit;
use std::os::raw::{c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::ptr;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::memory::MemoryState;
use crate::util::{
check_stack, get_internal_metatable, get_internal_userdata, init_internal_metatable,
push_internal_userdata, push_string, push_table, rawset_field, to_string, TypeKey,
DESTRUCTED_USERDATA_METATABLE,
};
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
pub(crate) enum WrappedFailure {
None,
Error(Error),
Panic(Option<Box<dyn Any + Send + 'static>>),
}
impl TypeKey for WrappedFailure {
#[inline(always)]
fn type_key() -> *const c_void {
&WRAPPED_FAILURE_TYPE_KEY as *const u8 as *const c_void
}
}
impl WrappedFailure {
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
#[cfg(feature = "luau")]
let ud = ffi::lua_newuserdata_t::<Self>(state);
#[cfg(not(feature = "luau"))]
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
ptr::write(ud, WrappedFailure::None);
ud
}
}
// In the context of a lua callback, this will call the given function and if the given function
// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
// the Rust side, it will resume the panic.
//
// This function assumes the structure of the stack at the beginning of a callback, that the only
// elements on the stack are the arguments to the callback.
//
// This function uses some of the bottom of the stack for error handling, the given callback will be
// given the number of arguments available as an argument, and should return the number of returns
// as normal, but cannot assume that the arguments available start at 0.
unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
where
F: FnOnce(c_int) -> Result<R>,
{
let nargs = ffi::lua_gettop(state);
// We need 2 extra stack spaces to store preallocated memory and error/panic metatable
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
ffi::luaL_checkstack(
state,
extra_stack,
cstr!("not enough stack space for callback error handling"),
);
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
// to store a wrapped error or panic *before* we proceed.
let ud = WrappedFailure::new_userdata(state);
ffi::lua_rotate(state, 1, 1);
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
Ok(Ok(r)) => {
ffi::lua_remove(state, 1);
r
}
Ok(Err(err)) => {
ffi::lua_settop(state, 1);
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
let traceback = to_string(state, -1);
ffi::lua_pop(state, 1);
traceback
} else {
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
ptr::write(ud, wrapped_error);
get_internal_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
Err(p) => {
ffi::lua_settop(state, 1);
ptr::write(ud, WrappedFailure::Panic(Some(p)));
get_internal_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
}
}
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
// error at the top of the stack:
// 1) If the error is actually a panic, this will continue the panic.
// 2) If the error on the top of the stack is actually an error, just returns it.
// 3) Otherwise, interprets the error as the appropriate lua error.
// Uses 2 stack spaces, does not call checkstack.
pub(crate) unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
mlua_debug_assert!(
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
"pop_error called with non-error return code"
);
match get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
Some(WrappedFailure::Error(err)) => {
ffi::lua_pop(state, 1);
err.clone()
}
Some(WrappedFailure::Panic(panic)) => {
if let Some(p) = panic.take() {
resume_unwind(p);
} else {
Error::PreviouslyResumedPanic
}
}
_ => {
let err_string = to_string(state, -1);
ffi::lua_pop(state, 1);
match err_code {
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
ffi::LUA_ERRSYNTAX => {
Error::SyntaxError {
// This seems terrible, but as far as I can tell, this is exactly what the
// stock Lua REPL does.
incomplete_input: err_string.ends_with("<eof>") || err_string.ends_with("'<eof>'"),
message: err_string,
}
}
ffi::LUA_ERRERR => {
// This error is raised when the error handler raises an error too many times
// recursively, and continuing to trigger the error handler would cause a stack
// overflow. It is not very useful to differentiate between this and "ordinary"
// runtime errors, so we handle them the same way.
Error::RuntimeError(err_string)
}
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
#[cfg(any(feature = "lua53", feature = "lua52"))]
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
_ => mlua_panic!("unrecognized lua error code"),
}
}
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be
// longjmping, should not contain any values that implements Drop.
// Internally uses 2 extra stack spaces, and does not call checkstack.
pub(crate) unsafe fn protect_lua_call(
state: *mut ffi::lua_State,
nargs: c_int,
f: unsafe extern "C-unwind" fn(*mut ffi::lua_State) -> c_int,
) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
});
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
ffi::lua_remove(state, stack_start + 1);
if ret == ffi::LUA_OK {
Ok(())
} else {
Err(pop_error(state, ret))
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
// given function return type is not the return value count, instead the inner function return
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since
// it will generally be longjmping, should not contain any values that implements Drop.
// Internally uses 3 extra stack spaces, and does not call checkstack.
pub(crate) unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State,
nargs: c_int,
nresults: c_int,
f: F,
) -> Result<R>
where
F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
{
struct Params<F, R: Copy> {
function: F,
result: MaybeUninit<R>,
nresults: c_int,
}
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where
F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
{
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1);
(*params).result.write(((*params).function)(state));
if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state)
} else {
(*params).nresults
}
}
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, do_call::<F, R>);
});
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let mut params = Params {
function: f,
result: MaybeUninit::uninit(),
nresults,
};
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
ffi::lua_remove(state, stack_start + 1);
if ret == ffi::LUA_OK {
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
// `params.result` is definitely initialized.
Ok(params.result.assume_init())
} else {
Err(pop_error(state, ret))
}
}
pub(crate) unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
// Luau calls error handler for memory allocation errors, skip it
// See https://github.com/Roblox/luau/issues/880
#[cfg(feature = "luau")]
if MemoryState::limit_reached(state) {
return 0;
}
if ffi::lua_checkstack(state, 2) == 0 {
// If we don't have enough stack space to even check the error type, do
// nothing so we don't risk shadowing a rust panic.
return 1;
}
if get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, s, 0);
ffi::lua_remove(state, -2);
}
}
1
}
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
pub(crate) unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
// Move error object to the main thread to safely call `__tostring` metamethod if present
ffi::lua_xmove(thread, state, 1);
if get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, thread, s, 0);
ffi::lua_remove(state, -2);
}
}
}
// Initialize the error, panic, and destructed userdata metatables.
pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
check_stack(state, 7)?;
// Create error and panic metatables
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
unsafe extern "C-unwind" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| {
check_stack(state, 3)?;
let err_buf = match get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
Some(WrappedFailure::Error(error)) => {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
ffi::lua_pop(state, 2);
(*err_buf).clear();
// Depending on how the API is used and what error types scripts are given, it may
// be possible to make this consume arbitrary amounts of memory (for example, some
// kind of recursive error structure?)
let _ = write!(&mut (*err_buf), "{error}");
Ok(err_buf)
}
Some(WrappedFailure::Panic(Some(ref panic))) => {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
(*err_buf).clear();
ffi::lua_pop(state, 2);
if let Some(msg) = panic.downcast_ref::<&str>() {
let _ = write!(&mut (*err_buf), "{msg}");
} else if let Some(msg) = panic.downcast_ref::<String>() {
let _ = write!(&mut (*err_buf), "{msg}");
} else {
let _ = write!(&mut (*err_buf), "<panic>");
};
Ok(err_buf)
}
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
_ => {
// I'm not sure whether this is possible to trigger without bugs in mlua?
Err(Error::UserDataTypeMismatch)
}
}?;
push_string(state, (*err_buf).as_bytes(), true)?;
(*err_buf).clear();
Ok(1)
})
}
init_internal_metatable::<WrappedFailure>(
state,
Some(|state| {
ffi::lua_pushcfunction(state, error_tostring);
rawset_field(state, -2, "__tostring")
}),
)?;
// Create destructed userdata metatable
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| Err(Error::CallbackDestructed))
}
push_table(state, 0, 26, true)?;
ffi::lua_pushcfunction(state, destructed_error);
for &method in &[
"__add",
"__sub",
"__mul",
"__div",
"__mod",
"__pow",
"__unm",
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
"__idiv",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__band",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__bor",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__bxor",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__bnot",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__shl",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__shr",
"__concat",
"__len",
"__eq",
"__lt",
"__le",
"__index",
"__newindex",
"__call",
"__tostring",
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
"__pairs",
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
"__ipairs",
#[cfg(feature = "luau")]
"__iter",
#[cfg(feature = "lua54")]
"__close",
] {
ffi::lua_pushvalue(state, -1);
rawset_field(state, -3, method)?;
}
ffi::lua_pop(state, 1);
protect_lua!(state, 1, 0, fn(state) {
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
})?;
// Create error print buffer
init_internal_metatable::<String>(state, None)?;
push_internal_userdata(state, String::new(), true)?;
protect_lua!(state, 1, 0, fn(state) {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
})?;
Ok(())
}
+41 -832
View File
@@ -1,45 +1,43 @@
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::ffi::CStr;
use std::fmt::Write;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc;
use std::os::raw::{c_char, c_int};
use std::{ptr, slice, str};
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use crate::error::{Error, Result};
use crate::memory::MemoryState;
pub(crate) use error::{
error_traceback, error_traceback_thread, init_error_registry, pop_error, protect_lua_call,
protect_lua_closure, WrappedFailure,
};
pub(crate) use short_names::short_type_name;
pub(crate) use types::TypeKey;
pub(crate) use userdata::{
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
init_internal_metatable, init_userdata_metatable, push_internal_userdata, take_userdata,
DESTRUCTED_USERDATA_METATABLE,
};
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
crate::lua::init_metatable_cache(&mut map);
map.insert(TypeId::of::<WrappedFailure>(), 0);
map.insert(TypeId::of::<String>(), 0);
map
});
#[cfg(not(feature = "lua54"))]
pub(crate) use userdata::push_userdata;
#[cfg(feature = "lua54")]
pub(crate) use userdata::push_userdata_uv;
#[cfg(not(feature = "luau"))]
pub(crate) use userdata::userdata_destructor;
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message.
#[inline]
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
pub(crate) unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
// when there is a way to be confident about stack safety and test it, this could be enabled
// only when `cfg!(debug_assertions)` is true.
mlua_assert!(
ffi::lua_checkstack(state, amount) != 0,
"out of stack space"
);
mlua_assert!(ffi::lua_checkstack(state, amount) != 0, "out of stack space");
}
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
#[inline]
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
pub(crate) unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
if ffi::lua_checkstack(state, amount) == 0 {
Err(Error::StackError)
} else {
@@ -47,7 +45,7 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
}
}
pub struct StackGuard {
pub(crate) struct StackGuard {
state: *mut ffi::lua_State,
top: c_int,
}
@@ -57,7 +55,7 @@ impl StackGuard {
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
// the beginning, this is considered a fatal logic error and will result in a panic.
#[inline]
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
pub(crate) unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
@@ -65,7 +63,8 @@ impl StackGuard {
}
// Same as `new()`, but allows specifying the expected stack size at the end of the scope.
pub const fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
#[inline]
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
StackGuard { state, top }
}
}
@@ -84,164 +83,9 @@ impl Drop for StackGuard {
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
// should not contain any values that implements Drop.
// Internally uses 2 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua_call(
state: *mut ffi::lua_State,
nargs: c_int,
f: unsafe extern "C-unwind" fn(*mut ffi::lua_State) -> c_int,
) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
});
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
ffi::lua_remove(state, stack_start + 1);
if ret == ffi::LUA_OK {
Ok(())
} else {
Err(pop_error(state, ret))
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
// given function return type is not the return value count, instead the inner function return
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
// will generally be lonjmping, should not contain any values that implements Drop.
// Internally uses 3 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State,
nargs: c_int,
nresults: c_int,
f: F,
) -> Result<R>
where
F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
{
struct Params<F, R: Copy> {
function: F,
result: MaybeUninit<R>,
nresults: c_int,
}
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where
F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
{
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1);
(*params).result.write(((*params).function)(state));
if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state)
} else {
(*params).nresults
}
}
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, do_call::<F, R>);
});
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let mut params = Params {
function: f,
result: MaybeUninit::uninit(),
nresults,
};
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
ffi::lua_remove(state, stack_start + 1);
if ret == ffi::LUA_OK {
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
// `params.result` is definitely initialized.
Ok(params.result.assume_init())
} else {
Err(pop_error(state, ret))
}
}
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
// error at the top of the stack:
// 1) If the error is actually a WrappedPanic, this will continue the panic.
// 2) If the error on the top of the stack is actually a WrappedError, just returns it.
// 3) Otherwise, interprets the error as the appropriate lua error.
// Uses 2 stack spaces, does not call checkstack.
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
mlua_debug_assert!(
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
"pop_error called with non-error return code"
);
match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
Some(WrappedFailure::Error(err)) => {
ffi::lua_pop(state, 1);
err.clone()
}
Some(WrappedFailure::Panic(panic)) => {
if let Some(p) = panic.take() {
resume_unwind(p);
} else {
Error::PreviouslyResumedPanic
}
}
_ => {
let err_string = to_string(state, -1);
ffi::lua_pop(state, 1);
match err_code {
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
ffi::LUA_ERRSYNTAX => {
Error::SyntaxError {
// This seems terrible, but as far as I can tell, this is exactly what the
// stock Lua REPL does.
incomplete_input: err_string.ends_with("<eof>")
|| err_string.ends_with("'<eof>'"),
message: err_string,
}
}
ffi::LUA_ERRERR => {
// This error is raised when the error handler raises an error too many times
// recursively, and continuing to trigger the error handler would cause a stack
// overflow. It is not very useful to differentiate between this and "ordinary"
// runtime errors, so we handle them the same way.
Error::RuntimeError(err_string)
}
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
#[cfg(any(feature = "lua53", feature = "lua52"))]
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
_ => mlua_panic!("unrecognized lua error code"),
}
}
}
}
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
#[inline(always)]
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
// Always use protected mode if the string is too long
if protect || s.len() > (1 << 30) {
protect_lua!(state, 0, 1, |state| {
@@ -256,7 +100,7 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
// Uses 3 stack spaces (when protect), does not call checkstack.
#[cfg(feature = "luau")]
#[inline(always)]
pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
let data = if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
} else {
@@ -269,7 +113,7 @@ pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(
pub(crate) unsafe fn push_table(
state: *mut ffi::lua_State,
narr: usize,
nrec: usize,
@@ -286,7 +130,7 @@ pub unsafe fn push_table(
}
// Uses 4 stack spaces, does not call checkstack.
pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
ffi::lua_pushvalue(state, table);
protect_lua!(state, 2, 0, |state| {
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
@@ -295,441 +139,8 @@ pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str
})
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
#[cfg(not(feature = "luau"))]
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
})?
} else {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
};
#[cfg(feature = "luau")]
let ud = if protect {
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
} else {
ffi::lua_newuserdata_t::<T>(state)
};
ptr::write(ud, t);
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
};
ptr::write(ud, t);
Ok(())
}
#[inline]
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
ud
}
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
// previously invalidated, and this method does not check for this.
// Uses 1 extra stack space and does not call checkstack.
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// We set the metatable of userdata on __gc to a special table with no __gc method and with
// metamethods that trigger an error on access. We do this so that it will not be double
// dropped, and also so that it cannot be used or identified as any particular userdata type
// after the first call to __gc.
get_destructed_userdata_metatable(state);
ffi::lua_setmetatable(state, -2);
let ud = get_userdata::<T>(state, -1);
// Update userdata tag to disable destructor and mark as destructed
#[cfg(feature = "luau")]
ffi::lua_setuserdatatag(state, -1, 1);
ffi::lua_pop(state, 1);
ptr::read(ud)
}
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack.
pub unsafe fn push_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
t: T,
protect: bool,
) -> Result<()> {
push_userdata(state, t, protect)?;
get_gc_metatable::<T>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
pub unsafe fn get_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
index: c_int,
mt_ptr: *const c_void,
) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
return ptr::null_mut();
}
if !mt_ptr.is_null() {
let ud_mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
if !ptr::eq(ud_mt_ptr, mt_ptr) {
return ptr::null_mut();
}
} else {
get_gc_metatable::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if res == 0 {
return ptr::null_mut();
}
}
ud
}
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_error(state);
}
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
1
}
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
1
}
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
return Ok(());
}
ffi::lua_pop(state, 1);
// Create and cache `__index` generator
let code = cstr!(
r#"
local error, isfunction, istable = ...
return function (__index, field_getters, methods)
-- Common case: has field getters and index is a table
if field_getters ~= nil and methods == nil and istable(__index) then
return function (self, key)
local field_getter = field_getters[key]
if field_getter ~= nil then
return field_getter(self)
end
return __index[key]
end
end
return function (self, key)
if field_getters ~= nil then
local field_getter = field_getters[key]
if field_getter ~= nil then
return field_getter(self)
end
end
if methods ~= nil then
local method = methods[key]
if method ~= nil then
return method
end
end
if isfunction(__index) then
return __index(self, key)
elseif __index == nil then
error("attempt to get an unknown field '"..key.."'")
else
return __index[key]
end
end
end
"#
);
let code_len = CStr::from_ptr(code).to_bytes().len();
protect_lua!(state, 0, 1, |state| {
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_index"));
if ret != ffi::LUA_OK {
ffi::lua_error(state);
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_pushcfunction(state, lua_istable_impl);
ffi::lua_call(state, 3, 1);
#[cfg(feature = "luau-jit")]
if ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
}
// Store in the registry
ffi::lua_pushvalue(state, -1);
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
})
}
pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
return Ok(());
}
ffi::lua_pop(state, 1);
// Create and cache `__newindex` generator
let code = cstr!(
r#"
local error, isfunction = ...
return function (__newindex, field_setters)
return function (self, key, value)
if field_setters ~= nil then
local field_setter = field_setters[key]
if field_setter ~= nil then
field_setter(self, value)
return
end
end
if isfunction(__newindex) then
__newindex(self, key, value)
elseif __newindex == nil then
error("attempt to set an unknown field '"..key.."'")
else
__newindex[key] = value
end
end
end
"#
);
let code_len = CStr::from_ptr(code).to_bytes().len();
protect_lua!(state, 0, 1, |state| {
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_newindex"));
if ret != ffi::LUA_OK {
ffi::lua_error(state);
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_call(state, 2, 1);
#[cfg(feature = "luau-jit")]
if ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
}
// Store in the registry
ffi::lua_pushvalue(state, -1);
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
})
}
// Populates the given table with the appropriate members to be a userdata metatable for the given type.
// This function takes the given table at the `metatable` index, and adds an appropriate `__gc` member
// to it for the given type and a `__metatable` entry to protect the table from script access.
// The function also, if given a `field_getters` or `methods` tables, will create an `__index` metamethod
// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
// captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
// Internally uses 9 stack spaces and does not call checkstack.
pub unsafe fn init_userdata_metatable(
state: *mut ffi::lua_State,
metatable: c_int,
field_getters: Option<c_int>,
field_setters: Option<c_int>,
methods: Option<c_int>,
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
ffi::lua_pushvalue(state, metatable);
if field_getters.is_some() || methods.is_some() {
// Push `__index` generator function
init_userdata_metatable_index(state)?;
push_string(state, b"__index", true)?;
let index_type = ffi::lua_rawget(state, -3);
match index_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
for &idx in &[field_getters, methods] {
if let Some(idx) = idx {
ffi::lua_pushvalue(state, idx);
} else {
ffi::lua_pushnil(state);
}
}
// Generate `__index`
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
}
_ => mlua_panic!("improper __index type {}", index_type),
}
rawset_field(state, -2, "__index")?;
}
if let Some(field_setters) = field_setters {
// Push `__newindex` generator function
init_userdata_metatable_newindex(state)?;
push_string(state, b"__newindex", true)?;
let newindex_type = ffi::lua_rawget(state, -3);
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
ffi::lua_pushvalue(state, field_setters);
// Generate `__newindex`
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
}
_ => mlua_panic!("improper __newindex type {}", newindex_type),
}
rawset_field(state, -2, "__newindex")?;
}
// Additional initialization
if let Some(extra_init) = extra_init {
extra_init(state)?;
}
ffi::lua_pushboolean(state, 0);
rawset_field(state, -2, "__metatable")?;
ffi::lua_pop(state, 1);
Ok(())
}
#[cfg(not(feature = "luau"))]
pub unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
// It's probably NOT a good idea to catch Rust panics in finalizer
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
take_userdata::<T>(state);
0
}
// In the context of a lua callback, this will call the given function and if the given function
// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
// the Rust side, it will resume the panic.
//
// This function assumes the structure of the stack at the beginning of a callback, that the only
// elements on the stack are the arguments to the callback.
//
// This function uses some of the bottom of the stack for error handling, the given callback will be
// given the number of arguments available as an argument, and should return the number of returns
// as normal, but cannot assume that the arguments available start at 0.
pub unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
where
F: FnOnce(c_int) -> Result<R>,
{
let nargs = ffi::lua_gettop(state);
// We need 2 extra stack spaces to store preallocated memory and error/panic metatable
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
ffi::luaL_checkstack(
state,
extra_stack,
cstr!("not enough stack space for callback error handling"),
);
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
// to store a wrapped error or panic *before* we proceed.
let ud = WrappedFailure::new_userdata(state);
ffi::lua_rotate(state, 1, 1);
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
Ok(Ok(r)) => {
ffi::lua_remove(state, 1);
r
}
Ok(Err(err)) => {
ffi::lua_settop(state, 1);
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
let traceback = to_string(state, -1);
ffi::lua_pop(state, 1);
traceback
} else {
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
ptr::write(ud, wrapped_error);
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
Err(p) => {
ffi::lua_settop(state, 1);
ptr::write(ud, WrappedFailure::Panic(Some(p)));
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
}
}
pub unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
// Luau calls error handler for memory allocation errors, skip it
// See https://github.com/Roblox/luau/issues/880
#[cfg(feature = "luau")]
if MemoryState::limit_reached(state) {
return 0;
}
if ffi::lua_checkstack(state, 2) == 0 {
// If we don't have enough stack space to even check the error type, do
// nothing so we don't risk shadowing a rust panic.
return 1;
}
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, s, 0);
ffi::lua_remove(state, -2);
}
}
1
}
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
// Move error object to the main thread to safely call `__tostring` metamethod if present
ffi::lua_xmove(thread, state, 1);
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, thread, s, 0);
ffi::lua_remove(state, -2);
}
}
}
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
pub(crate) unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
let top = ffi::lua_gettop(state);
@@ -743,9 +154,8 @@ pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int
ffi::lua_insert(state, 1);
ffi::lua_gettop(state)
} else {
if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{
let wf_ud = get_internal_userdata::<WrappedFailure>(state, -1, ptr::null());
if let Some(WrappedFailure::Panic(_)) = wf_ud.as_ref() {
ffi::lua_error(state);
}
ffi::lua_pushboolean(state, 0);
@@ -755,13 +165,12 @@ pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int
}
// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`.
pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
pub(crate) unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C-unwind" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{
let wf_ud = get_internal_userdata::<WrappedFailure>(state, -1, ptr::null());
if let Some(WrappedFailure::Panic(_)) = wf_ud.as_ref() {
1
} else {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
@@ -789,9 +198,8 @@ pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int
ffi::lua_insert(state, 2);
ffi::lua_gettop(state) - 1
} else {
if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{
let wf_ud = get_internal_userdata::<WrappedFailure>(state, -1, ptr::null());
if let Some(WrappedFailure::Panic(_)) = wf_ud.as_ref() {
ffi::lua_error(state);
}
ffi::lua_pushboolean(state, 0);
@@ -802,7 +210,7 @@ pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
// Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
@@ -825,198 +233,6 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
Some(ffi::lua_mainthread(state))
}
// Initialize the internal (with __gc method) metatable for a type T.
// Uses 6 stack spaces and calls checkstack.
pub unsafe fn init_gc_metatable<T: Any>(
state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
check_stack(state, 6)?;
push_table(state, 0, 3, true)?;
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
rawset_field(state, -2, "__gc")?;
}
ffi::lua_pushboolean(state, 0);
rawset_field(state, -2, "__metatable")?;
if let Some(f) = customize_fn {
f(state)?;
}
let type_id = TypeId::of::<T>();
let ref_addr = &METATABLE_CACHE[&type_id] as *const u8;
protect_lua!(state, 1, 0, |state| {
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
})?;
Ok(())
}
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
let type_id = TypeId::of::<T>();
let ref_addr =
mlua_expect!(METATABLE_CACHE.get(&type_id), "gc metatable does not exist") as *const u8;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
}
// Initialize the error, panic, and destructed userdata metatables.
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
check_stack(state, 7)?;
// Create error and panic metatables
unsafe extern "C-unwind" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| {
check_stack(state, 3)?;
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
Some(WrappedFailure::Error(error)) => {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
ffi::lua_pop(state, 2);
(*err_buf).clear();
// Depending on how the API is used and what error types scripts are given, it may
// be possible to make this consume arbitrary amounts of memory (for example, some
// kind of recursive error structure?)
let _ = write!(&mut (*err_buf), "{error}");
Ok(err_buf)
}
Some(WrappedFailure::Panic(Some(ref panic))) => {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
(*err_buf).clear();
ffi::lua_pop(state, 2);
if let Some(msg) = panic.downcast_ref::<&str>() {
let _ = write!(&mut (*err_buf), "{msg}");
} else if let Some(msg) = panic.downcast_ref::<String>() {
let _ = write!(&mut (*err_buf), "{msg}");
} else {
let _ = write!(&mut (*err_buf), "<panic>");
};
Ok(err_buf)
}
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
_ => {
// I'm not sure whether this is possible to trigger without bugs in mlua?
Err(Error::UserDataTypeMismatch)
}
}?;
push_string(state, (*err_buf).as_bytes(), true)?;
(*err_buf).clear();
Ok(1)
})
}
init_gc_metatable::<WrappedFailure>(
state,
Some(|state| {
ffi::lua_pushcfunction(state, error_tostring);
rawset_field(state, -2, "__tostring")
}),
)?;
// Create destructed userdata metatable
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| Err(Error::CallbackDestructed))
}
push_table(state, 0, 26, true)?;
ffi::lua_pushcfunction(state, destructed_error);
for &method in &[
"__add",
"__sub",
"__mul",
"__div",
"__mod",
"__pow",
"__unm",
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
"__idiv",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__band",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__bor",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__bxor",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__bnot",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__shl",
#[cfg(any(feature = "lua54", feature = "lua53"))]
"__shr",
"__concat",
"__len",
"__eq",
"__lt",
"__le",
"__index",
"__newindex",
"__call",
"__tostring",
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
"__pairs",
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
"__ipairs",
#[cfg(feature = "luau")]
"__iter",
#[cfg(feature = "lua54")]
"__close",
] {
ffi::lua_pushvalue(state, -1);
rawset_field(state, -3, method)?;
}
ffi::lua_pop(state, 1);
protect_lua!(state, 1, 0, fn(state) {
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
})?;
// Create error print buffer
init_gc_metatable::<String>(state, None)?;
push_gc_userdata(state, String::new(), true)?;
protect_lua!(state, 1, 0, fn(state) {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
})?;
Ok(())
}
pub(crate) enum WrappedFailure {
None,
Error(Error),
Panic(Option<Box<dyn Any + Send + 'static>>),
}
impl WrappedFailure {
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
#[cfg(feature = "luau")]
let ud = ffi::lua_newuserdata_t::<Self>(state);
#[cfg(not(feature = "luau"))]
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
ptr::write(ud, WrappedFailure::None);
ud
}
}
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
// panicking.
pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
@@ -1065,11 +281,6 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
}
}
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
if input.is_null() {
return None;
@@ -1091,9 +302,7 @@ pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
}
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
static USERDATA_METATABLE_INDEX: u8 = 0;
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
mod error;
mod short_names;
mod types;
mod userdata;
+6 -14
View File
@@ -25,22 +25,17 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
// Collapse everything up to the next special character,
// then skip over it
if let Some(special_character_index) = rest_of_string
.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
if let Some(special_character_index) =
rest_of_string.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
{
let segment_to_collapse = rest_of_string
.get(0..special_character_index)
.unwrap_or_default();
let segment_to_collapse = rest_of_string.get(0..special_character_index).unwrap_or_default();
parsed_name += collapse_type_name(segment_to_collapse);
// Insert the special character
let special_character =
&rest_of_string[special_character_index..=special_character_index];
let special_character = &rest_of_string[special_character_index..=special_character_index];
parsed_name.push_str(special_character);
match special_character {
">" | ")" | "]"
if rest_of_string[special_character_index + 1..].starts_with("::") =>
{
">" | ")" | "]" if rest_of_string[special_character_index + 1..].starts_with("::") => {
parsed_name.push_str("::");
// Move the index past the "::"
index += special_character_index + 3;
@@ -77,9 +72,6 @@ mod tests {
short_type_name::<HashMap<String, Option<[i32; 3]>>>(),
"HashMap<String, Option<[i32; 3]>>"
);
assert_eq!(
short_type_name::<dyn Fn(i32) -> i32>(),
"dyn Fn(i32) -> i32"
);
assert_eq!(short_type_name::<dyn Fn(i32) -> i32>(), "dyn Fn(i32) -> i32");
}
}
+82
View File
@@ -0,0 +1,82 @@
use std::any::Any;
use std::os::raw::c_void;
use crate::types::{Callback, CallbackUpvalue};
#[cfg(feature = "async")]
use crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue};
pub(crate) trait TypeKey: Any {
fn type_key() -> *const c_void;
}
static STRING_TYPE_KEY: u8 = 0;
impl TypeKey for String {
#[inline(always)]
fn type_key() -> *const c_void {
&STRING_TYPE_KEY as *const u8 as *const c_void
}
}
static CALLBACK_TYPE_KEY: u8 = 0;
impl TypeKey for Callback<'static> {
#[inline(always)]
fn type_key() -> *const c_void {
&CALLBACK_TYPE_KEY as *const u8 as *const c_void
}
}
static CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
impl TypeKey for CallbackUpvalue {
#[inline(always)]
fn type_key() -> *const c_void {
&CALLBACK_UPVALUE_TYPE_KEY as *const u8 as *const c_void
}
}
#[cfg(feature = "async")]
static ASYNC_CALLBACK_TYPE_KEY: u8 = 0;
#[cfg(feature = "async")]
impl TypeKey for AsyncCallback<'static> {
#[inline(always)]
fn type_key() -> *const c_void {
&ASYNC_CALLBACK_TYPE_KEY as *const u8 as *const c_void
}
}
#[cfg(feature = "async")]
static ASYNC_CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
#[cfg(feature = "async")]
impl TypeKey for AsyncCallbackUpvalue {
#[inline(always)]
fn type_key() -> *const c_void {
&ASYNC_CALLBACK_UPVALUE_TYPE_KEY as *const u8 as *const c_void
}
}
#[cfg(feature = "async")]
static ASYNC_POLL_UPVALUE_TYPE_KEY: u8 = 0;
#[cfg(feature = "async")]
impl TypeKey for AsyncPollUpvalue {
#[inline(always)]
fn type_key() -> *const c_void {
&ASYNC_POLL_UPVALUE_TYPE_KEY as *const u8 as *const c_void
}
}
#[cfg(feature = "async")]
static WAKER_TYPE_KEY: u8 = 0;
#[cfg(feature = "async")]
impl TypeKey for Option<std::task::Waker> {
#[inline(always)]
fn type_key() -> *const c_void {
&WAKER_TYPE_KEY as *const u8 as *const c_void
}
}
+380
View File
@@ -0,0 +1,380 @@
use std::ffi::CStr;
use std::os::raw::{c_int, c_void};
use std::{ptr, str};
use crate::error::Result;
use crate::util::{check_stack, push_string, push_table, rawset_field, TypeKey};
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_internal_userdata<T: TypeKey>(
state: *mut ffi::lua_State,
t: T,
protect: bool,
) -> Result<()> {
push_userdata(state, t, protect)?;
get_internal_metatable::<T>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
}
#[track_caller]
pub(crate) unsafe fn get_internal_metatable<T: TypeKey>(state: *mut ffi::lua_State) {
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
debug_assert!(ffi::lua_isnil(state, -1) == 0, "internal metatable not found");
}
// Initialize the internal metatable for a type T (with __gc method).
// Uses 6 stack spaces and calls checkstack.
pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
check_stack(state, 6)?;
push_table(state, 0, 3, true)?;
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
rawset_field(state, -2, "__gc")?;
}
ffi::lua_pushboolean(state, 0);
rawset_field(state, -2, "__metatable")?;
if let Some(f) = customize_fn {
f(state)?;
}
protect_lua!(state, 1, 0, |state| {
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
})?;
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
state: *mut ffi::lua_State,
index: c_int,
type_mt_ptr: *const c_void,
) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
return ptr::null_mut();
}
if !type_mt_ptr.is_null() {
let ud_mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
if ud_mt_ptr != type_mt_ptr {
return ptr::null_mut();
}
} else {
get_internal_metatable::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if res == 0 {
return ptr::null_mut();
}
}
ud
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
#[cfg(not(feature = "luau"))]
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
})?
} else {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
};
#[cfg(feature = "luau")]
let ud = if protect {
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
} else {
ffi::lua_newuserdata_t::<T>(state)
};
ptr::write(ud, t);
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub(crate) unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
};
ptr::write(ud, t);
Ok(())
}
#[inline]
pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
ud
}
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
// previously invalidated, and this method does not check for this.
// Uses 1 extra stack space and does not call checkstack.
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// We set the metatable of userdata on __gc to a special table with no __gc method and with
// metamethods that trigger an error on access. We do this so that it will not be double
// dropped, and also so that it cannot be used or identified as any particular userdata type
// after the first call to __gc.
get_destructed_userdata_metatable(state);
ffi::lua_setmetatable(state, -2);
let ud = get_userdata::<T>(state, -1);
// Update userdata tag to disable destructor and mark as destructed
#[cfg(feature = "luau")]
ffi::lua_setuserdatatag(state, -1, 1);
ffi::lua_pop(state, 1);
ptr::read(ud)
}
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
// Populates the given table with the appropriate members to be a userdata metatable for the given
// type. This function takes the given table at the `metatable` index, and adds an appropriate
// `__gc` member to it for the given type and a `__metatable` entry to protect the table from script
// access. The function also, if given a `field_getters` or `methods` tables, will create an
// `__index` metamethod (capturing previous one) to lookup in `field_getters` first, then `methods`
// and falling back to the captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
// Internally uses 9 stack spaces and does not call checkstack.
pub(crate) unsafe fn init_userdata_metatable(
state: *mut ffi::lua_State,
metatable: c_int,
field_getters: Option<c_int>,
field_setters: Option<c_int>,
methods: Option<c_int>,
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<()> {
ffi::lua_pushvalue(state, metatable);
if field_getters.is_some() || methods.is_some() {
// Push `__index` generator function
init_userdata_metatable_index(state)?;
push_string(state, b"__index", true)?;
let index_type = ffi::lua_rawget(state, -3);
match index_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
for &idx in &[field_getters, methods] {
if let Some(idx) = idx {
ffi::lua_pushvalue(state, idx);
} else {
ffi::lua_pushnil(state);
}
}
// Generate `__index`
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
}
_ => mlua_panic!("improper __index type {}", index_type),
}
rawset_field(state, -2, "__index")?;
}
if let Some(field_setters) = field_setters {
// Push `__newindex` generator function
init_userdata_metatable_newindex(state)?;
push_string(state, b"__newindex", true)?;
let newindex_type = ffi::lua_rawget(state, -3);
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
ffi::lua_pushvalue(state, field_setters);
// Generate `__newindex`
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
}
_ => mlua_panic!("improper __newindex type {}", newindex_type),
}
rawset_field(state, -2, "__newindex")?;
}
// Additional initialization
if let Some(extra_init) = extra_init {
extra_init(state)?;
}
ffi::lua_pushboolean(state, 0);
rawset_field(state, -2, "__metatable")?;
ffi::lua_pop(state, 1);
Ok(())
}
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_error(state);
}
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
1
}
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
1
}
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
return Ok(());
}
ffi::lua_pop(state, 1);
// Create and cache `__index` generator
let code = cstr!(
r#"
local error, isfunction, istable = ...
return function (__index, field_getters, methods)
-- Common case: has field getters and index is a table
if field_getters ~= nil and methods == nil and istable(__index) then
return function (self, key)
local field_getter = field_getters[key]
if field_getter ~= nil then
return field_getter(self)
end
return __index[key]
end
end
return function (self, key)
if field_getters ~= nil then
local field_getter = field_getters[key]
if field_getter ~= nil then
return field_getter(self)
end
end
if methods ~= nil then
local method = methods[key]
if method ~= nil then
return method
end
end
if isfunction(__index) then
return __index(self, key)
elseif __index == nil then
error("attempt to get an unknown field '"..key.."'")
else
return __index[key]
end
end
end
"#
);
let code_len = CStr::from_ptr(code).to_bytes().len();
protect_lua!(state, 0, 1, |state| {
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_index"));
if ret != ffi::LUA_OK {
ffi::lua_error(state);
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_pushcfunction(state, lua_istable_impl);
ffi::lua_call(state, 3, 1);
#[cfg(feature = "luau-jit")]
if ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
}
// Store in the registry
ffi::lua_pushvalue(state, -1);
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
})
}
unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
return Ok(());
}
ffi::lua_pop(state, 1);
// Create and cache `__newindex` generator
let code = cstr!(
r#"
local error, isfunction = ...
return function (__newindex, field_setters)
return function (self, key, value)
if field_setters ~= nil then
local field_setter = field_setters[key]
if field_setter ~= nil then
field_setter(self, value)
return
end
end
if isfunction(__newindex) then
__newindex(self, key, value)
elseif __newindex == nil then
error("attempt to set an unknown field '"..key.."'")
else
__newindex[key] = value
end
end
end
"#
);
let code_len = CStr::from_ptr(code).to_bytes().len();
protect_lua!(state, 0, 1, |state| {
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_newindex"));
if ret != ffi::LUA_OK {
ffi::lua_error(state);
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_call(state, 2, 1);
#[cfg(feature = "luau-jit")]
if ffi::luau_codegen_supported() != 0 {
ffi::luau_codegen_compile(state, -1);
}
// Store in the registry
ffi::lua_pushvalue(state, -1);
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
})
}
#[cfg(not(feature = "luau"))]
pub(crate) unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
// It's probably NOT a good idea to catch Rust panics in finalizer
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
take_userdata::<T>(state);
0
}
pub(crate) static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static USERDATA_METATABLE_INDEX: u8 = 0;
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
+161 -223
View File
@@ -1,15 +1,23 @@
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::iter;
use std::ops::Index;
use std::collections::{vec_deque, HashSet, VecDeque};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
use std::sync::Arc;
use std::{fmt, mem, ptr, slice, str, vec};
use std::{fmt, mem, ptr, str};
use num_traits::FromPrimitive;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, RawLua};
use crate::string::{BorrowedStr, String};
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number, SubtypeId};
use crate::userdata::AnyUserData;
use crate::util::{check_stack, StackGuard};
#[cfg(feature = "serialize")]
use {
crate::table::SerializableTable,
@@ -18,21 +26,11 @@ use {
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number, SubtypeId};
use crate::userdata::AnyUserData;
use crate::util::{check_stack, StackGuard};
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, and doing so will result in a panic.
#[derive(Clone)]
pub enum Value<'lua> {
pub enum Value {
/// The Lua value `nil`.
Nil,
/// The Lua value `true` or `false`.
@@ -52,27 +50,27 @@ pub enum Value<'lua> {
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
String(String<'lua>),
String(String),
/// Reference to a Lua table.
Table(Table<'lua>),
Table(Table),
/// Reference to a Lua function (or closure).
Function(Function<'lua>),
Function(Function),
/// Reference to a Lua thread (or coroutine).
Thread(Thread<'lua>),
Thread(Thread),
/// Reference to a userdata object that holds a custom type which implements `UserData`.
/// Special builtin userdata types will be represented as other `Value` variants.
UserData(AnyUserData<'lua>),
UserData(AnyUserData),
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Error),
Error(Box<Error>),
}
pub use self::Value::Nil;
impl<'lua> Value<'lua> {
impl Value {
/// A special value (lightuserdata) to represent null value.
///
/// It can be used in Lua tables without downsides of `nil`.
pub const NULL: Value<'static> = Value::LightUserData(LightUserData(ptr::null_mut()));
pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
/// Returns type name of this value.
pub const fn type_name(&self) -> &'static str {
@@ -117,8 +115,8 @@ impl<'lua> Value<'lua> {
/// Converts the value to a generic C pointer.
///
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it returns NULL.
/// Different objects will give different pointers.
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it
/// returns NULL. Different objects will give different pointers.
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
@@ -137,7 +135,8 @@ impl<'lua> Value<'lua> {
/// Converts the value to a string.
///
/// If the value has a metatable with a `__tostring` method, then it will be called to get the result.
/// If the value has a metatable with a `__tostring` method, then it will be called to get the
/// result.
pub fn to_string(&self) -> Result<StdString> {
match self {
Value::Nil => Ok("nil".to_string()),
@@ -153,15 +152,16 @@ impl<'lua> Value<'lua> {
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r, ..)) => unsafe {
let state = r.lua.state();
let lua = r.lua.lock();
let state = lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
r.lua.push_ref(r);
lua.push_ref(r);
protect_lua!(state, 1, 1, fn(state) {
ffi::luaL_tolstring(state, -1, ptr::null_mut());
})?;
Ok(String(r.lua.pop_ref()).to_str()?.to_string())
Ok(String(lua.pop_ref()).to_str()?.to_string())
},
Value::Error(err) => Ok(err.to_string()),
}
@@ -329,19 +329,20 @@ impl<'lua> Value<'lua> {
}
}
/// Cast the value to [`str`].
/// Cast the value to [`BorrowedStr`].
///
/// If the value is a Lua [`String`], try to convert it to [`str`] or return `None` otherwise.
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
/// otherwise.
#[inline]
pub fn as_str(&self) -> Option<&str> {
pub fn as_str(&self) -> Option<BorrowedStr> {
self.as_string().and_then(|s| s.to_str().ok())
}
/// Cast the value to [`Cow<str>`].
/// Cast the value to [`StdString`].
///
/// If the value is a Lua [`String`], converts it to [`Cow<str>`] or returns `None` otherwise.
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
#[inline]
pub fn as_string_lossy(&self) -> Option<Cow<str>> {
pub fn as_string_lossy(&self) -> Option<StdString> {
self.as_string().map(|s| s.to_string_lossy())
}
@@ -441,7 +442,7 @@ impl<'lua> Value<'lua> {
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
#[doc(hidden)]
pub fn to_serializable(&self) -> SerializableValue<'_, 'lua> {
pub fn to_serializable(&self) -> SerializableValue {
SerializableValue::new(self, Default::default(), None)
}
@@ -477,7 +478,7 @@ impl<'lua> Value<'lua> {
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
// String
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(b.as_bytes()),
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
// Other variants can be randomly ordered
@@ -523,7 +524,7 @@ impl<'lua> Value<'lua> {
}
}
impl fmt::Debug for Value<'_> {
impl fmt::Debug for Value {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if fmt.alternate() {
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
@@ -546,7 +547,7 @@ impl fmt::Debug for Value<'_> {
}
}
impl<'lua> PartialEq for Value<'lua> {
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Nil, Value::Nil) => true,
@@ -568,7 +569,7 @@ impl<'lua> PartialEq for Value<'lua> {
}
}
impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
impl AsRef<Value> for Value {
#[inline]
fn as_ref(&self) -> &Self {
self
@@ -578,15 +579,15 @@ impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
/// A wrapped [`Value`] with customized serialization behavior.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub struct SerializableValue<'a, 'lua> {
value: &'a Value<'lua>,
pub struct SerializableValue<'a> {
value: &'a Value,
options: crate::serde::de::Options,
// In many cases we don't need `visited` map, so don't allocate memory by default
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for Value<'lua> {
impl Serialize for Value {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
SerializableValue::new(self, Default::default(), None).serialize(serializer)
@@ -594,10 +595,10 @@ impl<'lua> Serialize for Value<'lua> {
}
#[cfg(feature = "serialize")]
impl<'a, 'lua> SerializableValue<'a, 'lua> {
impl<'a> SerializableValue<'a> {
#[inline]
pub(crate) fn new(
value: &'a Value<'lua>,
value: &'a Value,
options: crate::serde::de::Options,
visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
) -> Self {
@@ -649,7 +650,7 @@ impl<'a, 'lua> SerializableValue<'a, 'lua> {
}
#[cfg(feature = "serialize")]
impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
impl<'a> Serialize for SerializableValue<'a> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
@@ -688,9 +689,9 @@ impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
}
/// Trait for types convertible to `Value`.
pub trait IntoLua<'lua>: Sized {
pub trait IntoLua: Sized {
/// Performs the conversion.
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
fn into_lua(self, lua: &Lua) -> Result<Value>;
/// Pushes the value into the Lua stack.
///
@@ -698,15 +699,15 @@ pub trait IntoLua<'lua>: Sized {
/// This method does not check Lua stack space.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
lua.push_value(self.into_lua(lua)?)
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_value(&self.into_lua(lua.lua())?)
}
}
/// Trait for types convertible from `Value`.
pub trait FromLua<'lua>: Sized {
pub trait FromLua: Sized {
/// Performs the conversion.
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
@@ -714,7 +715,7 @@ pub trait FromLua<'lua>: Sized {
/// `to` is a function name that received the argument.
#[doc(hidden)]
#[inline]
fn from_lua_arg(arg: Value<'lua>, i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
@@ -726,19 +727,14 @@ pub trait FromLua<'lua>: Sized {
/// Performs the conversion for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx), lua)
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx), lua.lua())
}
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_arg(
idx: c_int,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
@@ -750,191 +746,136 @@ pub trait FromLua<'lua>: Sized {
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Debug, Clone)]
pub struct MultiValue<'lua> {
vec: Vec<Value<'lua>>,
lua: Option<&'lua Lua>,
pub struct MultiValue {
deque: VecDeque<Value>,
// FIXME
// lua: Option<&'static Lua>,
}
impl Drop for MultiValue<'_> {
impl Drop for MultiValue {
fn drop(&mut self) {
if let Some(lua) = self.lua {
let vec = mem::take(&mut self.vec);
lua.push_multivalue_to_pool(vec);
}
// FIXME
// if let Some(lua) = self.lua {
// let vec = mem::take(&mut self.deque);
// lua.push_multivalue_to_pool(vec);
// }
}
}
impl<'lua> MultiValue<'lua> {
impl Default for MultiValue {
#[inline]
fn default() -> MultiValue {
MultiValue::new()
}
}
impl Deref for MultiValue {
type Target = VecDeque<Value>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.deque
}
}
impl DerefMut for MultiValue {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.deque
}
}
impl MultiValue {
/// Creates an empty `MultiValue` containing no values.
pub const fn new() -> MultiValue<'lua> {
pub const fn new() -> MultiValue {
MultiValue {
vec: Vec::new(),
lua: None,
deque: VecDeque::new(),
// lua: None,
}
}
/// Similar to `new` but can reuse previously used container with allocated capacity.
#[inline]
pub(crate) fn with_lua_and_capacity(lua: &'lua Lua, capacity: usize) -> MultiValue<'lua> {
let vec = lua
.pop_multivalue_from_pool()
.map(|mut vec| {
vec.reserve(capacity);
vec
})
.unwrap_or_else(|| Vec::with_capacity(capacity));
pub(crate) fn with_lua_and_capacity(_lua: &Lua, capacity: usize) -> MultiValue {
// FIXME
// let deque = lua
// .pop_multivalue_from_pool()
// .map(|mut deque| {
// if capacity > 0 {
// deque.reserve(capacity);
// }
// deque
// })
// .unwrap_or_else(|| VecDeque::with_capacity(capacity));
let deque = VecDeque::with_capacity(capacity);
MultiValue {
vec,
lua: Some(lua),
deque,
// lua: Some(lua),
}
}
#[inline]
pub(crate) fn extend_from_values(&mut self, iter: impl IntoIterator<Item = Result<Value>>) -> Result<()> {
for value in iter {
self.push_back(value?);
}
Ok(())
}
}
impl FromIterator<Value> for MultiValue {
#[inline]
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let deque = VecDeque::from_iter(iter);
MultiValue {
deque,
// lua: None,
}
}
}
impl<'lua> Default for MultiValue<'lua> {
#[inline]
fn default() -> MultiValue<'lua> {
MultiValue::new()
}
}
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
#[inline]
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue::from_vec(Vec::from_iter(iter))
}
}
impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>;
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
impl IntoIterator for MultiValue {
type Item = Value;
type IntoIter = vec_deque::IntoIter<Value>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let vec = mem::take(&mut self.vec);
let deque = mem::take(&mut self.deque);
mem::forget(self);
vec.into_iter().rev()
deque.into_iter()
}
}
impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type Item = &'a Value<'lua>;
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
impl<'a> IntoIterator for &'a MultiValue {
type Item = &'a Value;
type IntoIter = vec_deque::Iter<'a, Value>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.vec.iter().rev()
}
}
impl<'lua> Index<usize> for MultiValue<'lua> {
type Output = Value<'lua>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
if let Some(result) = self.get(index) {
result
} else {
panic!(
"index out of bounds: the len is {} but the index is {}",
self.len(),
index
)
}
}
}
impl<'lua> MultiValue<'lua> {
#[inline]
pub fn from_vec(mut vec: Vec<Value<'lua>>) -> MultiValue<'lua> {
vec.reverse();
MultiValue { vec, lua: None }
}
#[inline]
pub fn into_vec(mut self) -> Vec<Value<'lua>> {
let mut vec = mem::take(&mut self.vec);
mem::forget(self);
vec.reverse();
vec
}
#[inline]
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
if index < self.vec.len() {
return self.vec.get(self.vec.len() - index - 1);
}
None
}
#[inline]
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
self.vec.pop()
}
#[inline]
pub fn push_front(&mut self, value: Value<'lua>) {
self.vec.push(value);
}
#[inline]
pub fn clear(&mut self) {
self.vec.clear();
}
#[inline]
pub fn len(&self) -> usize {
self.vec.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
#[inline]
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.vec.iter().rev()
}
#[inline]
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
self.vec.drain(..).rev()
}
#[inline]
pub(crate) fn refill(
&mut self,
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
) -> Result<()> {
self.vec.clear();
for value in iter {
self.vec.push(value?);
}
self.vec.reverse();
Ok(())
self.deque.iter()
}
}
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of just
/// one. Any type that implements `IntoLua` will automatically implement this trait.
pub trait IntoLuaMulti<'lua>: Sized {
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
pub trait IntoLuaMulti: Sized {
/// Performs the conversion.
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
/// Pushes the values into the Lua stack.
///
/// Returns number of pushed values.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
let mut values = self.into_lua_multi(lua)?;
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let values = self.into_lua_multi(lua.lua())?;
let len: c_int = values.len().try_into().unwrap();
unsafe {
check_stack(lua.state(), len + 1)?;
for v in values.drain_all() {
lua.push_value(v)?;
for val in &values {
lua.push_value(val)?;
}
}
Ok(len)
@@ -945,14 +886,14 @@ pub trait IntoLuaMulti<'lua>: Sized {
///
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
pub trait FromLuaMulti<'lua>: Sized {
pub trait FromLuaMulti: Sized {
/// Performs the conversion.
///
/// In case `values` contains more values than needed to perform the conversion, the excess
/// values should be ignored. This reflects the semantics of Lua when calling a function or
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
/// Performs the conversion for a list of arguments.
///
@@ -960,12 +901,7 @@ pub trait FromLuaMulti<'lua>: Sized {
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_args(
args: MultiValue<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(args, lua)
}
@@ -973,27 +909,22 @@ pub trait FromLuaMulti<'lua>: Sized {
/// Performs the conversion for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
let mut values = MultiValue::with_lua_and_capacity(lua, nvals as usize);
for idx in 1..=nvals {
values.push_front(lua.stack_value(-idx));
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
let mut values = MultiValue::with_lua_and_capacity(lua.lua(), nvals as usize);
for idx in 0..nvals {
values.push_back(lua.stack_value(-nvals + idx));
}
if nvals > 0 {
// It's safe to clear the stack as all references moved to ref thread
ffi::lua_pop(lua.state(), nvals);
}
Self::from_lua_multi(values, lua)
Self::from_lua_multi(values, lua.lua())
}
/// Same as `from_lua_args` but for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_args(
nargs: c_int,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
let _ = (i, to);
Self::from_stack_multi(nargs, lua)
}
@@ -1003,6 +934,13 @@ pub trait FromLuaMulti<'lua>: Sized {
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Value: Send);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(MultiValue: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Value: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
}
+16 -46
View File
@@ -6,8 +6,8 @@ use std::time::Duration;
use futures_util::stream::TryStreamExt;
use mlua::{
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt,
UserData, UserDataMethods, Value,
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt, UserData,
UserDataMethods, Value,
};
#[cfg(not(target_arch = "wasm32"))]
@@ -25,8 +25,7 @@ async fn sleep_ms(_ms: u64) {
async fn test_async_function() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
let f = lua.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
lua.globals().set("f", f)?;
let res: i64 = lua.load("f(1, 2, 3)").eval_async().await?;
@@ -75,9 +74,7 @@ async fn test_async_call() -> Result<()> {
match hello.call::<_, ()>("alex") {
Err(Error::RuntimeError(_)) => {}
_ => panic!(
"non-async executing async function must fail on the yield stage with RuntimeError"
),
_ => panic!("non-async executing async function must fail on the yield stage with RuntimeError"),
};
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
@@ -342,15 +339,9 @@ async fn test_async_table() -> Result<()> {
})?;
table.set("sleep", sleep)?;
assert_eq!(
table.call_async_method::<_, i64>("get_value", ()).await?,
10
);
assert_eq!(table.call_async_method::<_, i64>("get_value", ()).await?, 10);
table.call_async_method("set_value", 15).await?;
assert_eq!(
table.call_async_method::<_, i64>("get_value", ()).await?,
15
);
assert_eq!(table.call_async_method::<_, i64>("get_value", ()).await?, 15);
assert_eq!(
table.call_async_function::<_, String>("sleep", 7).await?,
"elapsed:7ms"
@@ -386,7 +377,7 @@ async fn test_async_userdata() -> Result<()> {
struct MyUserData(u64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
sleep_ms(10).await;
Ok(data.0)
@@ -411,17 +402,14 @@ async fn test_async_userdata() -> Result<()> {
});
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method(
mlua::MetaMethod::Index,
|_, data, key: String| async move {
sleep_ms(10).await;
match key.as_str() {
"ms" => Ok(Some(data.0 as f64)),
"s" => Ok(Some((data.0 as f64) / 1000.0)),
_ => Ok(None),
}
},
);
methods.add_async_meta_method(mlua::MetaMethod::Index, |_, data, key: String| async move {
sleep_ms(10).await;
match key.as_str() {
"ms" => Ok(Some(data.0 as f64)),
"s" => Ok(Some((data.0 as f64) / 1000.0)),
_ => Ok(None),
}
});
#[cfg(not(any(feature = "lua51", feature = "luau")))]
methods.add_async_meta_method_mut(
@@ -488,7 +476,7 @@ async fn test_async_thread_error() -> Result<()> {
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_meta_method("__tostring", |_, _this, ()| Ok("myuserdata error"))
}
}
@@ -507,24 +495,6 @@ async fn test_async_thread_error() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[tokio::test]
async fn test_owned_async_call() -> Result<()> {
let lua = Lua::new();
let hello = lua
.create_async_function(|_, name: String| async move {
sleep_ms(10).await;
Ok(format!("hello, {}!", name))
})?
.into_owned();
drop(lua);
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
Ok(())
}
#[tokio::test]
async fn test_async_terminate() -> Result<()> {
let lua = Lua::new();
+1 -2
View File
@@ -1,5 +1,4 @@
use std::fs;
use std::io;
use std::{fs, io};
use mlua::{Lua, Result};
+3 -123
View File
@@ -5,8 +5,7 @@ use std::ffi::{CStr, CString};
use bstr::BString;
use maplit::{btreemap, btreeset, hashmap, hashset};
use mlua::{
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef,
Value,
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef, Value,
};
#[test]
@@ -43,35 +42,6 @@ fn test_string_into_lua() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_string_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let s = lua.create_string("hello, world")?.into_owned();
let s2 = (&s).into_lua(&lua)?;
assert_eq!(s.to_ref(), *s2.as_string().unwrap());
// Push into stack
let table = lua.create_table()?;
table.set("s", &s)?;
assert_eq!(s.to_ref(), table.get::<_, String>("s")?);
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_string_from_lua() -> Result<()> {
let lua = Lua::new();
let s = lua.unpack::<mlua::OwnedString>(lua.pack("hello, world")?)?;
assert_eq!(s.to_ref(), "hello, world");
Ok(())
}
#[test]
fn test_table_into_lua() -> Result<()> {
let lua = Lua::new();
@@ -89,24 +59,6 @@ fn test_table_into_lua() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_table_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let t = lua.create_table()?.into_owned();
let t2 = (&t).into_lua(&lua)?;
assert_eq!(t.to_ref(), *t2.as_table().unwrap());
// Push into stack
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
f.call((&t, "hello"))?;
assert_eq!("hello", t.to_ref().get::<_, String>("s")?);
Ok(())
}
#[test]
fn test_function_into_lua() -> Result<()> {
let lua = Lua::new();
@@ -124,26 +76,6 @@ fn test_function_into_lua() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let f = lua
.create_function(|_, ()| Ok::<_, Error>(()))?
.into_owned();
let f2 = (&f).into_lua(&lua)?;
assert_eq!(f.to_ref(), *f2.as_function().unwrap());
// Push into stack
let table = lua.create_table()?;
table.set("f", &f)?;
assert_eq!(f.to_ref(), table.get::<_, Function>("f")?);
Ok(())
}
#[test]
fn test_thread_into_lua() -> Result<()> {
let lua = Lua::new();
@@ -162,36 +94,6 @@ fn test_thread_into_lua() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_thread_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
let th = lua.create_thread(f)?.into_owned();
let th2 = (&th).into_lua(&lua)?;
assert_eq!(&th.to_ref(), th2.as_thread().unwrap());
// Push into stack
let table = lua.create_table()?;
table.set("th", &th)?;
assert_eq!(th.to_ref(), table.get::<_, Thread>("th")?);
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_thread_from_lua() -> Result<()> {
let lua = Lua::new();
let th = lua.unpack::<mlua::OwnedThread>(Value::Thread(lua.current_thread()))?;
assert_eq!(th.to_ref(), lua.current_thread());
Ok(())
}
#[test]
fn test_anyuserdata_into_lua() -> Result<()> {
let lua = Lua::new();
@@ -210,25 +112,6 @@ fn test_anyuserdata_into_lua() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_anyuserdata_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let ud = lua.create_any_userdata(String::from("hello"))?.into_owned();
let ud2 = (&ud).into_lua(&lua)?;
assert_eq!(ud.to_ref(), *ud2.as_userdata().unwrap());
// Push into stack
let table = lua.create_table()?;
table.set("ud", &ud)?;
assert_eq!(ud.to_ref(), table.get::<_, AnyUserData>("ud")?);
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
Ok(())
}
#[test]
fn test_registry_value_into_lua() -> Result<()> {
let lua = Lua::new();
@@ -238,7 +121,7 @@ fn test_registry_value_into_lua() -> Result<()> {
let r = lua.create_registry_value(&s)?;
let value1 = lua.pack(&r)?;
let value2 = lua.pack(r)?;
assert_eq!(value1.as_str(), Some("hello, world"));
assert_eq!(value1.as_str().as_deref(), Some("hello, world"));
assert_eq!(value2.to_pointer(), value2.to_pointer());
// Push into stack
@@ -258,10 +141,7 @@ fn test_registry_value_into_lua() -> Result<()> {
// Check non-owned registry key
let lua2 = Lua::new();
let r2 = lua2.create_registry_value("abc")?;
assert!(matches!(
f.call::<_, ()>(&r2),
Err(Error::MismatchedRegistryKey)
));
assert!(matches!(f.call::<_, ()>(&r2), Err(Error::MismatchedRegistryKey)));
Ok(())
}
+5 -9
View File
@@ -6,9 +6,8 @@ use mlua::{Error, ErrorContext, Lua, Result};
fn test_error_context() -> Result<()> {
let lua = Lua::new();
let func = lua.create_function(|_, ()| {
Err::<(), _>(Error::runtime("runtime error")).context("some context")
})?;
let func =
lua.create_function(|_, ()| Err::<(), _>(Error::runtime("runtime error")).context("some context"))?;
lua.globals().set("func", func)?;
let msg = lua
@@ -33,12 +32,9 @@ fn test_error_context() -> Result<()> {
// Rewrite context message and test `downcast_ref`
let func3 = lua.create_function(|_, ()| {
Err::<(), _>(Error::external(io::Error::new(
io::ErrorKind::Other,
"other",
)))
.context("some context")
.context("some new context")
Err::<(), _>(Error::external(io::Error::new(io::ErrorKind::Other, "other")))
.context("some context")
.context("some new context")
})?;
let res = func3.call::<_, ()>(()).err().unwrap();
let Error::CallbackError { cause, .. } = &res else {
+2 -48
View File
@@ -43,10 +43,7 @@ fn test_bind() -> Result<()> {
concat = concat.bind("bar")?;
concat = concat.bind(("baz", "baf"))?;
assert_eq!(concat.call::<_, String>(())?, "foobarbazbaf");
assert_eq!(
concat.call::<_, String>(("hi", "wut"))?,
"foobarbazbafhiwut"
);
assert_eq!(concat.call::<_, String>(("hi", "wut"))?, "foobarbazbafhiwut");
let mut concat2 = globals.get::<_, Function>("concat")?;
concat2 = concat2.bind(())?;
@@ -271,8 +268,7 @@ fn test_function_wrap() -> Result<()> {
let lua = Lua::new();
lua.globals()
.set("f", Function::wrap(|_, s: String| Ok(s)))?;
lua.globals().set("f", Function::wrap(|_, s: String| Ok(s)))?;
lua.load(r#"assert(f("hello") == "hello")"#).exec().unwrap();
let mut _i = false;
@@ -296,45 +292,3 @@ fn test_function_wrap() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_function(|_, ()| Ok("hello, world!"))?
.into_owned();
drop(lua);
// We still should be able to call the function despite Lua is dropped
let s = f.call::<_, String>(())?;
assert_eq!(s.to_string_lossy(), "hello, world!");
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_function_drop() -> Result<()> {
let rc = std::sync::Arc::new(());
{
let lua = Lua::new();
lua.set_app_data(rc.clone());
let f1 = lua
.create_function(|_, ()| Ok("hello, world!"))?
.into_owned();
let f2 =
lua.create_function(move |_, ()| f1.to_ref().call::<_, std::string::String>(()))?;
assert_eq!(f2.call::<_, String>(())?.to_string_lossy(), "hello, world!");
}
// Check that Lua is properly destroyed
// It works because we collect garbage when Lua goes out of scope
assert_eq!(std::sync::Arc::strong_count(&rc), 1);
Ok(())
}
+11 -25
View File
@@ -75,15 +75,9 @@ fn test_function_calls() -> Result<()> {
let output = output.lock().unwrap();
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(
*output,
vec![(None, "main"), (Some("len".to_string()), "Lua")]
);
assert_eq!(*output, vec![(None, "main"), (Some("len".to_string()), "Lua")]);
} else {
assert_eq!(
*output,
vec![(None, "main"), (Some("len".to_string()), "C")]
);
assert_eq!(*output, vec![(None, "main"), (Some("len".to_string()), "C")]);
}
Ok(())
@@ -97,10 +91,7 @@ fn test_error_within_hook() -> Result<()> {
Err(Error::runtime("Something happened in there!"))
});
let err = lua
.load("x = 1")
.exec()
.expect_err("panic didn't propagate");
let err = lua.load("x = 1").exec().expect_err("panic didn't propagate");
match err {
Error::CallbackError { cause, .. } => match cause.deref() {
@@ -153,14 +144,9 @@ fn test_limit_execution_instructions() -> Result<()> {
fn test_hook_removal() -> Result<()> {
let lua = Lua::new();
lua.set_hook(
HookTriggers::new().every_nth_instruction(1),
|_lua, _debug| {
Err(Error::runtime(
"this hook should've been removed by this time",
))
},
);
lua.set_hook(HookTriggers::new().every_nth_instruction(1), |_lua, _debug| {
Err(Error::runtime("this hook should've been removed by this time"))
});
assert!(lua.load("local x = 1").exec().is_err());
lua.remove_hook();
@@ -189,9 +175,10 @@ fn test_hook_swap_within_hook() -> Result<()> {
.set_hook(HookTriggers::EVERY_LINE, move |lua, _debug| {
lua.globals().set("ok", 1i64)?;
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().set_hook(
HookTriggers::EVERY_LINE,
move |lua, _debug| {
tl.borrow()
.as_ref()
.unwrap()
.set_hook(HookTriggers::EVERY_LINE, move |lua, _debug| {
lua.load(
r#"
if ok ~= nil then
@@ -205,8 +192,7 @@ fn test_hook_swap_within_hook() -> Result<()> {
tl.borrow().as_ref().unwrap().remove_hook();
});
Ok(())
},
)
})
});
Ok(())
})
+4 -9
View File
@@ -7,17 +7,14 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use mlua::{
Compiler, CoverageInfo, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value,
Vector, VmState,
Compiler, CoverageInfo, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector,
VmState,
};
#[test]
fn test_version() -> Result<()> {
let lua = Lua::new();
assert!(lua
.globals()
.get::<_, String>("_VERSION")?
.starts_with("Luau 0."));
assert!(lua.globals().get::<_, String>("_VERSION")?.starts_with("Luau 0."));
Ok(())
}
@@ -189,9 +186,7 @@ fn test_vector_metatable() -> Result<()> {
lua.set_vector_metatable(Some(vector_mt.clone()));
lua.globals().set("Vector3", vector_mt)?;
let compiler = Compiler::new()
.set_vector_lib("Vector3")
.set_vector_ctor("new");
let compiler = Compiler::new().set_vector_lib("Vector3").set_vector_ctor("new");
// Test vector methods (fastcall)
lua.load(
+1 -6
View File
@@ -68,12 +68,7 @@ fn test_gc_control() -> Result<()> {
assert_eq!(lua.gc_inc(0, 0, 0), GCMode::Generational);
}
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
{
assert!(lua.gc_is_running());
lua.gc_stop();
View File
+40 -46
View File
@@ -4,8 +4,8 @@ use std::collections::HashMap;
use std::error::Error as StdError;
use mlua::{
DeserializeOptions, Error, ExternalResult, Lua, LuaSerdeExt, Result as LuaResult,
SerializeOptions, UserData, Value,
DeserializeOptions, Error, ExternalResult, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions,
UserData, Value,
};
use serde::{Deserialize, Serialize};
@@ -71,49 +71,49 @@ fn test_serialize() -> Result<(), Box<dyn StdError>> {
Ok(())
}
#[test]
fn test_serialize_in_scope() -> LuaResult<()> {
#[derive(Serialize, Clone)]
struct MyUserData(i64, String);
// #[test]
// fn test_serialize_in_scope() -> LuaResult<()> {
// #[derive(Serialize, Clone)]
// struct MyUserData(i64, String);
impl UserData for MyUserData {}
// impl UserData for MyUserData {}
let lua = Lua::new();
lua.scope(|scope| {
let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
assert_eq!(
serde_json::to_value(&ud).unwrap(),
serde_json::json!((-5, "test userdata"))
);
Ok(())
})?;
// let lua = Lua::new();
// lua.scope(|scope| {
// let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
// assert_eq!(
// serde_json::to_value(&ud).unwrap(),
// serde_json::json!((-5, "test userdata"))
// );
// Ok(())
// })?;
lua.scope(|scope| {
let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
lua.globals().set("ud", ud)
})?;
let val = lua.load("ud").eval::<Value>()?;
match serde_json::to_value(&val) {
Ok(v) => panic!("expected destructed error, got {}", v),
Err(e) if e.to_string().contains("destructed") => {}
Err(e) => panic!("expected destructed error, got {}", e),
}
// lua.scope(|scope| {
// let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
// lua.globals().set("ud", ud)
// })?;
// let val = lua.load("ud").eval::<Value>()?;
// match serde_json::to_value(&val) {
// Ok(v) => panic!("expected destructed error, got {}", v),
// Err(e) if e.to_string().contains("destructed") => {}
// Err(e) => panic!("expected destructed error, got {}", e),
// }
struct MyUserDataRef<'a>(#[allow(unused)] &'a ());
// struct MyUserDataRef<'a>(#[allow(unused)] &'a ());
impl<'a> UserData for MyUserDataRef<'a> {}
// impl<'a> UserData for MyUserDataRef<'a> {}
lua.scope(|scope| {
let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
match serde_json::to_value(&ud) {
Ok(v) => panic!("expected serialization error, got {}", v),
Err(serde_json::Error { .. }) => {}
};
Ok(())
})?;
// lua.scope(|scope| {
// let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
// match serde_json::to_value(&ud) {
// Ok(v) => panic!("expected serialization error, got {}", v),
// Err(serde_json::Error { .. }) => {}
// };
// Ok(())
// })?;
Ok(())
}
// Ok(())
// }
#[test]
fn test_serialize_any_userdata() -> Result<(), Box<dyn StdError>> {
@@ -395,10 +395,7 @@ fn test_to_value_with_options() -> Result<(), Box<dyn StdError>> {
unit: (),
unitstruct: UnitStruct,
};
let data2 = lua.to_value_with(
&mydata,
SerializeOptions::new().serialize_none_to_null(false),
)?;
let data2 = lua.to_value_with(&mydata, SerializeOptions::new().serialize_none_to_null(false))?;
globals.set("data2", data2)?;
lua.load(
r#"
@@ -410,10 +407,7 @@ fn test_to_value_with_options() -> Result<(), Box<dyn StdError>> {
.exec()?;
// serialize_unit_to_null
let data3 = lua.to_value_with(
&mydata,
SerializeOptions::new().serialize_unit_to_null(false),
)?;
let data3 = lua.to_value_with(&mydata, SerializeOptions::new().serialize_unit_to_null(false))?;
globals.set("data3", data3)?;
lua.load(
r#"
+11 -12
View File
@@ -7,7 +7,7 @@ fn test_static_lua() -> Result<()> {
let lua = Lua::new().into_static();
thread_local! {
static TABLE: RefCell<Option<Table<'static>>> = RefCell::new(None);
static TABLE: RefCell<Option<Table>> = RefCell::new(None);
}
let f = lua.create_function(|_, table: Table| {
@@ -38,7 +38,7 @@ fn test_static_lua_coroutine() -> Result<()> {
let lua = Lua::new().into_static();
thread_local! {
static TABLE: RefCell<Option<Table<'static>>> = RefCell::new(None);
static TABLE: RefCell<Option<Table>> = RefCell::new(None);
}
let f = lua.create_function(|_, table: Table| {
@@ -83,16 +83,15 @@ async fn test_static_async() -> Result<()> {
tokio::task::yield_now().await;
}
let timer =
lua.create_async_function(|_, (i, n, f): (u64, u64, mlua::Function)| async move {
tokio::task::spawn_local(async move {
for _ in 0..n {
tokio::task::spawn_local(f.call_async::<(), ()>(()));
sleep_ms(i).await;
}
});
Ok(())
})?;
let timer = lua.create_async_function(|_, (i, n, f): (u64, u64, mlua::Function)| async move {
tokio::task::spawn_local(async move {
for _ in 0..n {
tokio::task::spawn_local(f.call_async::<(), ()>(()));
sleep_ms(i).await;
}
});
Ok(())
})?;
lua.globals().set("timer", timer)?;
{
+8 -35
View File
@@ -15,9 +15,7 @@ fn test_string_compare() {
with_str("teststring", |t| assert_eq!(t, b"teststring".to_vec())); // Vec<u8>
with_str("teststring", |t| assert_eq!(t, "teststring".to_string())); // String
with_str("teststring", |t| assert_eq!(t, t)); // mlua::String
with_str("teststring", |t| {
assert_eq!(t, Cow::from(b"teststring".as_ref()))
}); // Cow (borrowed)
with_str("teststring", |t| assert_eq!(t, Cow::from(b"teststring".as_ref()))); // Cow (borrowed)
with_str("bla", |t| assert_eq!(t, Cow::from(b"bla".to_vec()))); // Cow (owned)
}
@@ -40,14 +38,8 @@ fn test_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?"[..]
);
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?"[..]);
assert!(err.to_str().is_err());
assert_eq!(err.as_bytes(), &b"but \xff isn't :("[..]);
@@ -75,11 +67,11 @@ fn test_string_hash() -> Result<()> {
let set: HashSet<String> = lua.load(r#"{"hello", "world", "abc", 321}"#).eval()?;
assert_eq!(set.len(), 4);
assert!(set.contains(b"hello".as_ref()));
assert!(set.contains(b"world".as_ref()));
assert!(set.contains(b"abc".as_ref()));
assert!(set.contains(b"321".as_ref()));
assert!(!set.contains(b"Hello".as_ref()));
assert!(set.contains(&lua.create_string("hello")?));
assert!(set.contains(&lua.create_string("world")?));
assert!(set.contains(&lua.create_string("abc")?));
assert!(set.contains(&lua.create_string("321")?));
assert!(!set.contains(&lua.create_string("Hello")?));
Ok(())
}
@@ -111,22 +103,3 @@ fn test_string_pointer() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_string() -> Result<()> {
let lua = Lua::new();
let s = lua.create_string("hello, world!")?.into_owned();
drop(lua);
// Shortcuts
assert_eq!(s.as_bytes(), b"hello, world!");
assert_eq!(s.to_str()?, "hello, world!");
assert_eq!(format!("{s:?}"), "\"hello, world!\"");
// Access via reference
assert_eq!(s.to_ref().to_string_lossy(), "hello, world!");
Ok(())
}
+7 -42
View File
@@ -45,17 +45,11 @@ fn test_table() -> Result<()> {
assert_eq!(table1.len()?, 5);
assert!(!table1.is_empty());
assert_eq!(
table1
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
table1.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
);
assert_eq!(
table1
.clone()
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
table1.clone().sequence_values().collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3, 4, 5]
);
assert_eq!(table1, [1, 2, 3, 4, 5]);
@@ -63,10 +57,7 @@ fn test_table() -> Result<()> {
assert_eq!(table2.len()?, 0);
assert!(table2.is_empty());
assert_eq!(
table2
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
table2.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
vec![]
);
assert_eq!(table2, [0; 0]);
@@ -81,29 +72,20 @@ fn test_table() -> Result<()> {
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
let table4 = globals.get::<_, Table>("table4")?;
assert_eq!(
table4
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
);
table4.raw_insert(4, 35)?;
table4.raw_insert(7, 7)?;
assert_eq!(
table4
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 1), (2, 2), (3, 3), (4, 35), (5, 4), (6, 5), (7, 7)]
);
table4.raw_remove(1)?;
assert_eq!(
table4
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 2), (2, 3), (3, 35), (4, 4), (5, 5), (6, 7)]
);
@@ -448,24 +430,7 @@ fn test_table_call() -> Result<()> {
// Test calling non-callable table
let table2 = lua.create_table()?;
assert!(matches!(
table2.call::<_, ()>(()),
Err(Error::RuntimeError(_))
));
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_table() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table()?.into_owned();
drop(lua);
table.to_ref().set("abc", 123)?;
assert_eq!(table.to_ref().get::<_, i64>("abc")?, 123);
assert!(matches!(table2.call::<_, ()>(()), Err(Error::RuntimeError(_))));
Ok(())
}
+56 -92
View File
@@ -8,8 +8,8 @@ use std::sync::Arc;
use std::{error, f32, f64, fmt};
use mlua::{
ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table,
UserData, Value, Variadic,
ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table, UserData,
Value, Variadic,
};
#[cfg(not(feature = "luau"))]
@@ -17,7 +17,7 @@ use mlua::{
fn test_safety() -> Result<()> {
let lua = Lua::new();
assert!(lua.load(r#"require "debug""#).exec().is_err());
match lua.load_from_std_lib(StdLib::DEBUG) {
match lua.load_std_libs(StdLib::DEBUG) {
Err(Error::SafetyError(_)) => {}
Err(e) => panic!("expected SafetyError, got {:?}", e),
Ok(_) => panic!("expected SafetyError, got no error"),
@@ -53,7 +53,7 @@ fn test_safety() -> Result<()> {
// Test safety rules after dynamically loading `package` library
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
lua.load_from_std_lib(StdLib::PACKAGE)?;
lua.load_std_libs(StdLib::PACKAGE)?;
match lua.load(r#"package.loadlib()"#).exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::SafetyError(_) => {}
@@ -108,10 +108,7 @@ fn test_exec() -> Result<()> {
.eval()?;
println!("checkpoint");
assert!(module.contains_key("func")?);
assert_eq!(
module.get::<_, Function>("func")?.call::<_, String>(())?,
"hello"
);
assert_eq!(module.get::<_, Function>("func")?.call::<_, String>(())?, "hello");
Ok(())
}
@@ -128,10 +125,7 @@ fn test_eval() -> Result<()> {
incomplete_input: true,
..
}) => {}
r => panic!(
"expected SyntaxError with incomplete_input=true, got {:?}",
r
),
r => panic!("expected SyntaxError with incomplete_input=true, got {:?}", r),
}
Ok(())
@@ -141,10 +135,7 @@ fn test_eval() -> Result<()> {
fn test_load_mode() -> Result<()> {
let lua = unsafe { Lua::unsafe_new() };
assert_eq!(
lua.load("1 + 1").set_mode(ChunkMode::Text).eval::<i32>()?,
2
);
assert_eq!(lua.load("1 + 1").set_mode(ChunkMode::Text).eval::<i32>()?, 2);
match lua.load("1 + 1").set_mode(ChunkMode::Binary).exec() {
Ok(_) => panic!("expected SyntaxError, got no error"),
Err(Error::SyntaxError { message: msg, .. }) => {
@@ -158,12 +149,7 @@ fn test_load_mode() -> Result<()> {
#[cfg(feature = "luau")]
let bytecode = mlua::Compiler::new().compile("return 1 + 1");
assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2);
assert_eq!(
lua.load(&bytecode)
.set_mode(ChunkMode::Binary)
.eval::<i32>()?,
2
);
assert_eq!(lua.load(&bytecode).set_mode(ChunkMode::Binary).eval::<i32>()?, 2);
match lua.load(&bytecode).set_mode(ChunkMode::Text).exec() {
Ok(_) => panic!("expected SyntaxError, got no error"),
Err(Error::SyntaxError { message: msg, .. }) => {
@@ -308,8 +294,7 @@ fn test_error() -> Result<()> {
)
.exec()?;
let rust_error_function =
lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?;
let rust_error_function = lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?;
globals.set("rust_error_function", rust_error_function)?;
let no_error = globals.get::<_, Function>("no_error")?;
@@ -338,10 +323,7 @@ fn test_error() -> Result<()> {
let return_string_error = globals.get::<_, Function>("return_string_error")?;
assert!(return_string_error.call::<_, Error>(()).is_ok());
match lua
.load("if youre happy and you know it syntax error")
.exec()
{
match lua.load("if youre happy and you know it syntax error").exec() {
Err(Error::SyntaxError {
incomplete_input: false,
..
@@ -374,15 +356,13 @@ fn test_error() -> Result<()> {
fn test_panic() -> Result<()> {
fn make_lua(options: LuaOptions) -> Result<Lua> {
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let rust_panic_function =
lua.create_function(|_, msg: Option<StdString>| -> Result<()> {
if let Some(msg) = msg {
panic!("{}", msg)
}
panic!("rust panic")
})?;
lua.globals()
.set("rust_panic_function", rust_panic_function)?;
let rust_panic_function = lua.create_function(|_, msg: Option<StdString>| -> Result<()> {
if let Some(msg) = msg {
panic!("{}", msg)
}
panic!("rust panic")
})?;
lua.globals().set("rust_panic_function", rust_panic_function)?;
Ok(lua)
}
@@ -538,12 +518,7 @@ fn test_num_conversion() -> Result<()> {
assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0);
#[cfg(any(feature = "lua54", feature = "lua53"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1.0");
#[cfg(any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
))]
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1");
assert_eq!(lua.load("1.5").eval::<i64>()?, 1);
@@ -620,12 +595,7 @@ fn test_pcall_xpcall() -> Result<()> {
assert_eq!(globals.get::<_, String>("pcall_error")?, "testerror");
assert_eq!(globals.get::<_, bool>("xpcall_statusr")?, false);
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit"))]
assert_eq!(
globals.get::<_, std::string::String>("xpcall_error")?,
"testerror"
@@ -645,9 +615,7 @@ fn test_pcall_xpcall() -> Result<()> {
"#,
)
.exec()?;
let _ = globals
.get::<_, Function>("xpcall_recursion")?
.call::<_, ()>(());
let _ = globals.get::<_, Function>("xpcall_recursion")?.call::<_, ()>(());
Ok(())
}
@@ -657,7 +625,7 @@ fn test_recursive_mut_callback_error() -> Result<()> {
let lua = Lua::new();
let mut v = Some(Box::new(123));
let f = lua.create_function_mut::<_, (), _>(move |lua, mutate: bool| {
let f = lua.create_function_mut(move |lua, mutate: bool| {
if mutate {
v = None;
} else {
@@ -767,18 +735,18 @@ fn test_drop_registry_value() -> Result<()> {
fn test_replace_registry_value() -> Result<()> {
let lua = Lua::new();
let key = lua.create_registry_value::<i32>(42)?;
lua.replace_registry_value(&key, "new value")?;
let mut key = lua.create_registry_value::<i32>(42)?;
lua.replace_registry_value(&mut key, "new value")?;
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
lua.replace_registry_value(&key, Value::Nil)?;
lua.replace_registry_value(&mut key, Value::Nil)?;
assert_eq!(lua.registry_value::<Value>(&key)?, Value::Nil);
lua.replace_registry_value(&key, 123)?;
lua.replace_registry_value(&mut key, 123)?;
assert_eq!(lua.registry_value::<i32>(&key)?, 123);
let key2 = lua.create_registry_value(Value::Nil)?;
lua.replace_registry_value(&key2, Value::Nil)?;
let mut key2 = lua.create_registry_value(Value::Nil)?;
lua.replace_registry_value(&mut key2, Value::Nil)?;
assert_eq!(lua.registry_value::<Value>(&key2)?, Value::Nil);
lua.replace_registry_value(&key2, "abc")?;
lua.replace_registry_value(&mut key2, "abc")?;
assert_eq!(lua.registry_value::<String>(&key2)?, "abc");
Ok(())
@@ -901,10 +869,7 @@ fn test_application_data() -> Result<()> {
f.call(())?;
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4");
assert_eq!(
*lua.app_data_ref::<Vec<&str>>().unwrap(),
vec!["test2", "test3"]
);
assert_eq!(*lua.app_data_ref::<Vec<&str>>().unwrap(), vec!["test2", "test3"]);
lua.remove_app_data::<Vec<&str>>();
assert!(matches!(lua.app_data_ref::<Vec<&str>>(), None));
@@ -919,9 +884,7 @@ fn test_recursion() -> Result<()> {
let f = lua.create_function(move |lua, i: i32| {
if i < 64 {
lua.globals()
.get::<_, Function>("f")?
.call::<_, ()>(i + 1)?;
lua.globals().get::<_, Function>("f")?.call::<_, ()>(i + 1)?;
}
Ok(())
})?;
@@ -961,8 +924,7 @@ fn test_too_many_arguments() -> Result<()> {
fn test_too_many_recursions() -> Result<()> {
let lua = Lua::new();
let f = lua
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
let f = lua.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
lua.globals().set("f", &f)?;
assert!(f.call::<_, ()>(()).is_err());
@@ -985,9 +947,7 @@ fn test_too_many_binds() -> Result<()> {
let concat = globals.get::<_, Function>("f")?;
assert!(concat.bind(Variadic::from_iter(1..1000000)).is_err());
assert!(concat
.call::<_, ()>(Variadic::from_iter(1..1000000))
.is_err());
assert!(concat.call::<_, ()>(Variadic::from_iter(1..1000000)).is_err());
Ok(())
}
@@ -1038,10 +998,7 @@ fn test_large_args() -> Result<()> {
)
.eval()?;
assert_eq!(
f.call::<_, usize>((0..100).collect::<Variadic<usize>>())?,
4950
);
assert_eq!(f.call::<_, usize>((0..100).collect::<Variadic<usize>>())?, 4950);
Ok(())
}
@@ -1110,12 +1067,7 @@ fn test_context_thread() -> Result<()> {
)
.into_function()?;
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
f.call::<_, ()>(lua.current_thread())?;
#[cfg(any(
@@ -1154,10 +1106,7 @@ fn test_context_thread_51() -> Result<()> {
fn test_jit_version() -> Result<()> {
let lua = Lua::new();
let jit: Table = lua.globals().get("jit")?;
assert!(jit
.get::<_, String>("version")?
.to_str()?
.contains("LuaJIT"));
assert!(jit.get::<_, String>("version")?.to_str()?.contains("LuaJIT"));
Ok(())
}
@@ -1324,11 +1273,26 @@ fn test_luajit_cdata() -> Result<()> {
#[test]
#[cfg(feature = "send")]
#[cfg(not(target_arch = "wasm32"))]
fn test_send() {
fn test_multi_thread() -> Result<()> {
let lua = Lua::new();
std::thread::spawn(move || {
let _lua = lua;
})
.join()
.unwrap();
lua.globals().set("i", 0)?;
let func = lua.load("i = i + 1").into_function()?;
std::thread::scope(|s| {
s.spawn(|| {
for _ in 0..5 {
func.call::<_, ()>(()).unwrap();
}
});
s.spawn(|| {
for _ in 0..5 {
func.call::<_, ()>(()).unwrap();
}
});
});
assert_eq!(lua.globals().get::<_, i32>("i")?, 10);
Ok(())
}
-31
View File
@@ -231,34 +231,3 @@ fn test_thread_pointer() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_thread() -> Result<()> {
let lua = Lua::new();
let accumulate = lua
.create_thread(
lua.load(
r#"
function (sum)
while true do
sum = sum + coroutine.yield(sum)
end
end
"#,
)
.eval::<Function>()?,
)?
.into_owned();
for i in 0..4 {
accumulate.resume::<_, ()>(i)?;
}
assert_eq!(accumulate.resume::<_, i64>(4)?, 10);
assert_eq!(accumulate.status(), ThreadStatus::Resumable);
assert!(accumulate.resume::<_, ()>("error").is_err());
assert_eq!(accumulate.status(), ThreadStatus::Error);
Ok(())
}
+39 -215
View File
@@ -1,21 +1,13 @@
use std::collections::HashMap;
use std::string::String as StdString;
use std::sync::Arc;
#[cfg(not(feature = "parking_lot"))]
use std::sync::{Mutex, RwLock};
#[cfg(feature = "parking_lot")]
use parking_lot::{Mutex, RwLock};
#[cfg(not(feature = "send"))]
use std::{cell::RefCell, rc::Rc};
#[cfg(feature = "lua54")]
use std::sync::atomic::{AtomicI64, Ordering};
use mlua::{
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result,
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value, Variadic,
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String,
UserData, UserDataFields, UserDataMethods, UserDataRef, Value, Variadic,
};
#[test]
@@ -47,7 +39,7 @@ fn test_methods() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("get_value", |_, data, ()| Ok(data.0));
methods.add_method_mut("set_value", |_, data, args| {
data.0 = args;
@@ -97,7 +89,7 @@ fn test_method_variadic() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("get", |_, data, ()| Ok(data.0));
methods.add_method_mut("add", |_, data, vals: Variadic<i64>| {
data.0 += vals.into_iter().sum::<i64>();
@@ -122,19 +114,15 @@ fn test_metamethods() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("get", |_, data, ()| Ok(data.0));
methods.add_meta_function(
MetaMethod::Add,
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
Ok(MyUserData(lhs.0 + rhs.0))
},
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| Ok(MyUserData(lhs.0 + rhs.0)),
);
methods.add_meta_function(
MetaMethod::Sub,
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
Ok(MyUserData(lhs.0 - rhs.0))
},
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| Ok(MyUserData(lhs.0 - rhs.0)),
);
methods.add_meta_function(
MetaMethod::Eq,
@@ -147,22 +135,16 @@ fn test_metamethods() -> Result<()> {
Err("no such custom index".into_lua_err())
}
});
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
use std::iter::FromIterator;
let stateless_iter =
lua.create_function(|_, (data, i): (UserDataRef<Self>, i64)| {
let i = i + 1;
if i <= data.0 {
return Ok(mlua::Variadic::from_iter(vec![i, i]));
}
return Ok(mlua::Variadic::new());
})?;
let stateless_iter = lua.create_function(|_, (data, i): (UserDataRef<Self>, i64)| {
let i = i + 1;
if i <= data.0 {
return Ok(mlua::Variadic::from_iter(vec![i, i]));
}
return Ok(mlua::Variadic::new());
})?;
Ok((stateless_iter, data.clone(), 0))
});
}
@@ -180,12 +162,7 @@ fn test_metamethods() -> Result<()> {
10
);
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
let pairs_it = lua
.load(
r#"
@@ -210,12 +187,7 @@ fn test_metamethods() -> Result<()> {
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
let userdata2: Value = globals.get("userdata2")?;
@@ -241,7 +213,7 @@ fn test_metamethod_close() -> Result<()> {
struct MyUserData(Arc<AtomicI64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("get", |_, data, ()| Ok(data.0.load(Ordering::Relaxed)));
methods.add_meta_method(MetaMethod::Close, |_, data, _err: Value| {
data.0.store(0, Ordering::Relaxed);
@@ -287,7 +259,7 @@ fn test_gc_userdata() -> Result<()> {
}
impl UserData for MyUserdata {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("access", |_, this, ()| {
assert!(this.id == 123);
Ok(())
@@ -326,7 +298,7 @@ fn test_userdata_take() -> Result<()> {
struct MyUserdata(Arc<i64>);
impl UserData for MyUserdata {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("num", |_, this, ()| Ok(*this.0))
}
}
@@ -461,10 +433,8 @@ fn test_functions() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("get_value", |_, ud: AnyUserData| {
Ok(ud.borrow::<MyUserData>()?.0)
});
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_function("get_value", |_, ud: AnyUserData| Ok(ud.borrow::<MyUserData>()?.0));
methods.add_function_mut("set_value", |_, (ud, value): (AnyUserData, i64)| {
ud.borrow_mut::<MyUserData>()?.0 = value;
Ok(())
@@ -515,7 +485,7 @@ fn test_fields() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
fields.add_field("static", "constant");
fields.add_field_method_get("val", |_, data| Ok(data.0));
fields.add_field_method_set("val", |_, data, val| {
@@ -525,8 +495,7 @@ fn test_fields() -> Result<()> {
// Use userdata "uservalue" storage
fields.add_field_function_get("uval", |_, ud| ud.user_value::<Option<String>>());
fields
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
fields.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
fields.add_meta_field(MetaMethod::Index, HashMap::from([("f", 321)]));
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
@@ -562,12 +531,12 @@ fn test_fields() -> Result<()> {
struct MyUserData2(i64);
impl UserData for MyUserData2 {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
fields.add_field("z", 0);
fields.add_field_method_get("x", |_, data| Ok(data.0));
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Index, |_, _, name: StdString| match &*name {
"y" => Ok(Some(-1)),
_ => Ok(None),
@@ -594,7 +563,7 @@ fn test_metatable() -> Result<()> {
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_function("my_type_name", |_, data: AnyUserData| {
let metatable = data.get_metatable()?;
metatable.get::<String>(MetaMethod::Type)
@@ -605,8 +574,7 @@ fn test_metatable() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
globals.set("ud", MyUserData)?;
lua.load(r#"assert(ud:my_type_name() == "MyUserData")"#)
.exec()?;
lua.load(r#"assert(ud:my_type_name() == "MyUserData")"#).exec()?;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
lua.load(r#"assert(tostring(ud):sub(1, 11) == "MyUserData:")"#)
@@ -640,7 +608,7 @@ fn test_metatable() -> Result<()> {
struct MyUserData2;
impl UserData for MyUserData2 {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
fields.add_meta_field_with("__index", |_| Ok(1));
}
}
@@ -655,136 +623,14 @@ fn test_metatable() -> Result<()> {
struct MyUserData3;
impl UserData for MyUserData3 {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
fields.add_meta_field_with(MetaMethod::Type, |_| Ok("CustomName"));
}
}
let ud = lua.create_userdata(MyUserData3)?;
let metatable = ud.get_metatable()?;
assert_eq!(
metatable.get::<String>(MetaMethod::Type)?.to_str()?,
"CustomName"
);
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("static", "constant");
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();
// Rc<T>
#[cfg(not(feature = "send"))]
{
let ud = Rc::new(MyUserData(1));
globals.set("rc_ud", ud.clone())?;
lua.load(
r#"
assert(rc_ud.static == "constant")
local ok, err = pcall(function() rc_ud.data = 2 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
)
assert(rc_ud.data == 1)
"#,
)
.exec()?;
globals.set("rc_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&ud), 1);
}
// Rc<RefCell<T>>
#[cfg(not(feature = "send"))]
{
let ud = Rc::new(RefCell::new(MyUserData(1)));
globals.set("rc_refcell_ud", ud.clone())?;
lua.load(
r#"
assert(rc_refcell_ud.static == "constant")
rc_refcell_ud.data = rc_refcell_ud.data + 1
assert(rc_refcell_ud.data == 2)
"#,
)
.exec()?;
assert_eq!(ud.borrow().0, 2);
globals.set("rc_refcell_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&ud), 1);
}
// Arc<T>
let ud1 = Arc::new(MyUserData(2));
globals.set("arc_ud", ud1.clone())?;
lua.load(
r#"
assert(arc_ud.static == "constant")
local ok, err = pcall(function() arc_ud.data = 3 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
)
assert(arc_ud.data == 2)
"#,
)
.exec()?;
globals.set("arc_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud1), 1);
// Arc<Mutex<T>>
let ud2 = Arc::new(Mutex::new(MyUserData(2)));
globals.set("arc_mutex_ud", ud2.clone())?;
lua.load(
r#"
assert(arc_mutex_ud.static == "constant")
arc_mutex_ud.data = arc_mutex_ud.data + 1
assert(arc_mutex_ud.data == 3)
"#,
)
.exec()?;
#[cfg(not(feature = "parking_lot"))]
assert_eq!(ud2.lock().unwrap().0, 3);
#[cfg(feature = "parking_lot")]
assert_eq!(ud2.lock().0, 3);
globals.set("arc_mutex_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud2), 1);
// Arc<RwLock<T>>
let ud3 = Arc::new(RwLock::new(MyUserData(3)));
globals.set("arc_rwlock_ud", ud3.clone())?;
lua.load(
r#"
assert(arc_rwlock_ud.static == "constant")
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
assert(arc_rwlock_ud.data == 4)
"#,
)
.exec()?;
#[cfg(not(feature = "parking_lot"))]
assert_eq!(ud3.read().unwrap().0, 4);
#[cfg(feature = "parking_lot")]
assert_eq!(ud3.read().0, 4);
globals.set("arc_rwlock_ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud3), 1);
assert_eq!(metatable.get::<String>(MetaMethod::Type)?.to_str()?, "CustomName");
Ok(())
}
@@ -794,12 +640,12 @@ fn test_userdata_proxy() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
fields.add_field("static_field", 123);
fields.add_field_method_get("n", |_, this| Ok(this.0));
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_function("new", |_, n| Ok(Self(n)));
methods.add_method("plus", |_, this, n: i64| Ok(this.0 + n));
@@ -867,8 +713,7 @@ fn test_any_userdata_wrap() -> Result<()> {
reg.add_method("get", |_, this, ()| Ok(this.clone()));
})?;
lua.globals()
.set("s", AnyUserData::wrap("hello".to_string()))?;
lua.globals().set("s", AnyUserData::wrap("hello".to_string()))?;
lua.load(
r#"
assert(s:get() == "hello")
@@ -888,7 +733,7 @@ fn test_userdata_ext() -> Result<()> {
struct MyUserData(u32);
impl UserData for MyUserData {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
fields.add_field_method_get("n", |_, this| Ok(this.0));
fields.add_field_method_set("n", |_, this, val| {
this.0 = val;
@@ -896,7 +741,7 @@ fn test_userdata_ext() -> Result<()> {
});
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Call, |_, _this, ()| Ok("called"));
methods.add_method_mut("add", |_, this, x: u32| {
this.0 += x;
@@ -929,7 +774,7 @@ fn test_userdata_method_errors() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
methods.add_method("get_value", |_, data, ()| Ok(data.0));
}
}
@@ -967,25 +812,6 @@ fn test_userdata_pointer() -> Result<()> {
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_userdata() -> Result<()> {
let lua = Lua::new();
let ud = lua.create_any_userdata("abc")?.into_owned();
drop(lua);
assert_eq!(*ud.borrow::<&str>()?, "abc");
*ud.borrow_mut()? = "cba";
assert!(matches!(
ud.borrow::<i64>(),
Err(Error::UserDataTypeMismatch)
));
assert_eq!(ud.take::<&str>()?, "cba");
Ok(())
}
#[cfg(feature = "macros")]
#[test]
fn test_userdata_derive() -> Result<()> {
@@ -1000,8 +826,7 @@ fn test_userdata_derive() -> Result<()> {
reg.add_function("val", |_, this: MyUserData| Ok(this.0));
})?;
lua.globals()
.set("ud", AnyUserData::wrap(MyUserData(123)))?;
lua.globals().set("ud", AnyUserData::wrap(MyUserData(123)))?;
lua.load("assert(ud:val() == 123)").exec()?;
// More complex struct where generics and where clause
@@ -1015,8 +840,7 @@ fn test_userdata_derive() -> Result<()> {
reg.add_function("val", |_, this: MyUserData2<'static, i32>| Ok(*this.0));
})?;
lua.globals()
.set("ud", AnyUserData::wrap(MyUserData2(&321)))?;
lua.globals().set("ud", AnyUserData::wrap(MyUserData2(&321)))?;
lua.load("assert(ud:val() == 321)").exec()?;
Ok(())
+6 -13
View File
@@ -111,10 +111,7 @@ fn test_value_to_string() -> Result<()> {
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2, 13.3)).to_string()?,
"vector(10, 11.1, 12.2, 13.3)"
);
assert_eq!(
Value::String(lua.create_string("hello")?).to_string()?,
"hello"
);
assert_eq!(Value::String(lua.create_string("hello")?).to_string()?, "hello");
let table: Value = lua.load("{}").eval()?;
assert!(table.to_string()?.starts_with("table:"));
@@ -140,7 +137,7 @@ fn test_value_to_string() -> Result<()> {
let ud: Value = Value::UserData(lua.create_userdata(MyUserData)?);
assert!(ud.to_string()?.starts_with("MyUserData:"));
let err = Value::Error(Error::runtime("test error"));
let err = Value::Error(Box::new(Error::runtime("test error")));
assert_eq!(err.to_string()?, "runtime error: test error");
Ok(())
@@ -189,9 +186,7 @@ fn test_value_conversions() -> Result<()> {
assert_eq!(Value::Number(1.23).as_f64(), Some(1.23f64));
assert!(Value::String(lua.create_string("hello")?).is_string());
assert_eq!(
Value::String(lua.create_string("hello")?)
.as_string()
.unwrap(),
Value::String(lua.create_string("hello")?).as_string().unwrap(),
"hello"
);
assert_eq!(
@@ -207,11 +202,9 @@ fn test_value_conversions() -> Result<()> {
assert!(Value::Table(lua.create_table()?).is_table());
assert!(Value::Table(lua.create_table()?).as_table().is_some());
assert!(Value::Function(lua.create_function(|_, ()| Ok(())).unwrap()).is_function());
assert!(
Value::Function(lua.create_function(|_, ()| Ok(())).unwrap())
.as_function()
.is_some()
);
assert!(Value::Function(lua.create_function(|_, ()| Ok(())).unwrap())
.as_function()
.is_some());
assert!(Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?).is_thread());
assert!(
Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?)