Compare commits

..

16 Commits

Author SHA1 Message Date
Alex Orlenko 84811b1be3 v0.8.10 2023-08-16 00:20:05 +01:00
Alex Orlenko 032d4af896 Fix loading luau code starting with \t 2023-08-16 00:15:34 +01:00
Alex Orlenko c9099a4364 Update to Luau 0.590 2023-08-16 00:15:05 +01:00
Alex Orlenko e4eeee05c4 Pin (more strict) lua-src and luajit-src versions 2023-08-16 00:09:20 +01:00
Alex Orlenko 15e353a7f8 v0.8.9 2023-05-16 23:02:19 +01:00
Alex Orlenko 765117c2bb Update tarpaulin settings 2023-05-16 23:02:17 +01:00
Alex Orlenko 573d71345f Don't set html_root_url (it's not recommended) 2023-05-16 22:55:29 +01:00
Alex Orlenko 72de17bf47 Allow deserializing Lua null into unit(()) or unit struct. See #264 2023-05-16 22:53:37 +01:00
Alex Orlenko 5a96e80266 Use lua_closethread instead of lua_resetthread in vendored mode (introduced in Lua 5.4.6) 2023-05-16 22:50:46 +01:00
Alex Orlenko bfdb4087b8 Update minimal (vendored) Lua 5.4 to 5.4.6 2023-05-16 22:49:49 +01:00
Alex Orlenko eb84284824 Fix ref_stack_exhaustion test (Lua 5.4.6) 2023-05-16 22:12:36 +01:00
Alex Orlenko 34679e105d v0.8.8 2023-03-05 17:50:53 +00:00
Alex Orlenko bc194981fc Optimize userdata methods call when __index and fields_getters are nil 2023-03-05 14:43:12 +00:00
Alex Orlenko c9715aa5d9 Fix potential deadlock when trying to reuse dropped RegistryKey.
If no free registry id found, we call protect_lua! macro while keeping mutex guard to the unref list.
Protected calls can trigger garbage collection and if RegistryKey is placed in userdata being collected, this can lead to deadlock.
The solution is drop mutex guard as soon as possible.
Also this commit includes optimization in creating reference in Lua registry.
2023-03-05 14:39:22 +00:00
Alex Orlenko c108dc8213 Force protected mode for long enough strings 2023-03-05 14:35:15 +00:00
Alex Orlenko e86ef9d755 v0.8.7 2023-01-04 16:15:23 +00:00
60 changed files with 2505 additions and 3894 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- name: Generate coverage report
run: |
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files src/ffi/*/*
- name: Upload report to codecov.io
uses: codecov/codecov-action@v3
+8 -8
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,parking_lot"
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,parking_lot"
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,parking_lot"
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,parking_lot"
shell: bash
test:
@@ -122,13 +122,13 @@ 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,unstable"
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
shell: bash
- name: Run compile tests (macos lua54)
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
run: |
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" -- --ignored
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
shell: bash
test_with_sanitizer:
@@ -153,7 +153,7 @@ jobs:
- name: Run ${{ matrix.lua }} tests with address sanitizer
run: |
RUSTFLAGS="-Z sanitizer=address" \
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,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,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
shell: bash
test_modules:
@@ -230,4 +230,4 @@ jobs:
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
+8 -28
View File
@@ -1,34 +1,14 @@
## v0.9.0-beta.1
## v0.8.10
New features:
- Owned Lua types (unstable feature flag)
- New functions `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async`
- `Lua::register_userdata_type()` to register a custom userdata types (without requiring `UserData` trait)
- `Lua::create_any_userdata()`
- Added `create_userdata_ref`/`create_userdata_ref_mut` for scopes
- Added `AnyUserDataExt` trait with auxiliary functions for `AnyUserData`
- Added `UserDataRef` and `UserDataRefMut` type wrapped that implement `FromLua`
- Improved error handling:
* Improved error reporting when calling Rust functions from Lua.
* Added `Error::BadArgument` to help identify bad argument position or name
* Added `ErrorContext` extension trait to attach additional context to `Error`
- Update to Luau 0.590 (luau0-src to 0.7.x)
- Fix loading luau code starting with \t
- Pin lua-src and luajit-src versions
Breaking changes:
- Refactored `AsChunk` trait
- `ToLua`/`ToLuaMulti` renamed to `IntoLua`/`IntoLuaMulti`
- Renamed `to_lua_err` to `into_lua_err`
- Removed `FromLua` impl for `T: UserData+Clone`
- Removed `Lua::async_scope`
- Added `&Lua` arg to Luau interrupt callback
## v0.8.9
Other:
- Better Debug for String
- Allow deserializing values from serializable UserData using `Lua::from_value()` method
- Added `Table::clear()` method
- Added `Error::downcast_ref()` method
- Support setting memory limit for Lua 5.1/JIT/Luau
- Support setting module name in `#[lua_module(name = "...")]` macro
- Minor fixes and improvements
- Update minimal (vendored) Lua 5.4 to 5.4.6
- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
- Allow deserializing Lua null into unit (`()`) or unit struct.
## v0.8.8
+10 -13
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.9.0-beta.1" # remember to update html_root_url and mlua_derive
version = "0.8.10" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2021"
repository = "https://github.com/khvzak/mlua"
@@ -17,7 +17,7 @@ with async/await features and support of writing native Lua modules in Rust.
"""
[package.metadata.docs.rs]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot", "unstable"]
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
@@ -37,13 +37,12 @@ vendored = ["lua-src", "luajit-src"]
module = ["mlua_derive"]
async = ["futures-core", "futures-task", "futures-util"]
send = []
serialize = ["serde", "erased-serde", "serde-value"]
serialize = ["serde", "erased-serde"]
macros = ["mlua_derive/macros"]
unstable = []
[dependencies]
mlua_derive = { version = "=0.9.0-beta.1", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default_features = false }
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
rustc-hash = "1.0"
@@ -52,19 +51,18 @@ futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true }
serde = { version = "1.0", optional = true }
erased-serde = { version = "0.3", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", optional = true }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.0", optional = true }
lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 210.5.0", optional = true }
luau0-src = { version = "0.7.0", optional = true }
[dev-dependencies]
rustyline = "11.0"
criterion = { version = "0.4", features = ["html_reports", "async_tokio"] }
rustyline = "10.0"
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
trybuild = "1.0"
futures = "0.3.5"
hyper = { version = "0.14", features = ["client", "server"] }
@@ -75,7 +73,6 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
maplit = "1.0"
tempfile = "3"
static_assertions = "1.0"
[[bench]]
name = "benchmark"
+2 -7
View File
@@ -7,9 +7,9 @@
[crates.io]: https://crates.io/crates/mlua
[API Documentation]: https://docs.rs/mlua/badge.svg
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/v0.8/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/khvzak/mlua
[MSRV]: https://img.shields.io/badge/rust-1.63+-brightgreen.svg?&logo=rust
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
[Guided Tour] | [Benchmarks] | [FAQ]
@@ -17,10 +17,6 @@
[Benchmarks]: https://github.com/khvzak/script-bench-rs
[FAQ]: FAQ.md
> **Note**
>
> Please see the [v0.8](https://github.com/khvzak/mlua/tree/v0.8) branch for the stable versions of `mlua` released to crates.io.
`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.
@@ -52,7 +48,6 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
* `macros`: enable procedural macros (such as `chunk!`)
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
* `unstable`: enable **unstable** features. The public API of these features may break between releases.
[5.4]: https://www.lua.org/manual/5.4/manual.html
[5.3]: https://www.lua.org/manual/5.3/manual.html
+2 -2
View File
@@ -120,7 +120,7 @@ fn call_sum_callback(c: &mut Criterion) {
}
fn call_async_sum_callback(c: &mut Criterion) {
let options = LuaOptions::new().thread_pool_size(1024);
let options = LuaOptions::new().thread_cache_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let callback = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
@@ -272,7 +272,7 @@ fn call_async_userdata_method(c: &mut Criterion) {
}
}
let options = LuaOptions::new().thread_pool_size(1024);
let options = LuaOptions::new().thread_cache_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
lua.globals().set("userdata", UserData(10)).unwrap();
+4 -4
View File
@@ -8,7 +8,7 @@ fn get_env_var(name: &str) -> String {
match env::var(name) {
Ok(val) => val,
Err(env::VarError::NotPresent) => String::new(),
Err(err) => panic!("cannot get {name}: {err}"),
Err(err) => panic!("cannot get {}: {}", name, err),
}
}
@@ -37,8 +37,8 @@ pub fn probe_lua() -> Option<PathBuf> {
if get_env_var("LUA_LINK") == "static" {
link_lib = "static=";
};
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:rustc-link-lib={link_lib}{lua_lib}");
println!("cargo:rustc-link-search=native={}", lib_dir);
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
}
return Some(PathBuf::from(include_dir));
}
@@ -72,7 +72,7 @@ pub fn probe_lua() -> Option<PathBuf> {
.probe(alt_probe);
}
lua.unwrap_or_else(|_| panic!("cannot find Lua {ver} using `pkg-config`"))
lua.unwrap_or_else(|_| panic!("cannot find Lua {} using `pkg-config`", ver))
.include_paths
.get(0)
.cloned()
+4 -4
View File
@@ -12,7 +12,7 @@ impl UserData for BodyReader {
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
let mut reader = reader.borrow_mut::<Self>()?;
if let Some(bytes) = reader.0.data().await {
let bytes = bytes.into_lua_err()?;
let bytes = bytes.to_lua_err()?;
return Some(lua.create_string(&bytes)).transpose();
}
Ok(None)
@@ -26,8 +26,8 @@ async fn main() -> Result<()> {
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
let client = HyperClient::new();
let uri = uri.parse().into_lua_err()?;
let resp = client.get(uri).await.into_lua_err()?;
let uri = uri.parse().to_lua_err()?;
let resp = client.get(uri).await.to_lua_err()?;
let lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
@@ -37,7 +37,7 @@ async fn main() -> Result<()> {
headers
.entry(key.as_str())
.or_insert(Vec::new())
.push(value.to_str().into_lua_err()?);
.push(value.to_str().to_lua_err()?);
}
lua_resp.set("headers", headers)?;
+2 -2
View File
@@ -10,8 +10,8 @@ async fn main() -> Result<()> {
let resp = reqwest::get(&uri)
.await
.and_then(|resp| resp.error_for_status())
.into_lua_err()?;
let json = resp.json::<serde_json::Value>().await.into_lua_err()?;
.to_lua_err()?;
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
lua.to_value(&json)
})?;
+2 -14
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, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
fn main() -> Result<()> {
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
@@ -32,7 +30,7 @@ fn main() -> Result<()> {
global = 'foo'..'bar'
"#,
)
.set_name("example code")
.set_name("example code")?
.exec()?;
assert_eq!(globals.get::<_, String>("global")?, "foobar");
@@ -153,16 +151,6 @@ fn main() -> Result<()> {
#[derive(Copy, Clone)]
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> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => unreachable!(),
}
}
}
impl UserData for Vec2 {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("magnitude", |_, vec, ()| {
+1 -1
View File
@@ -2,7 +2,7 @@
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
edition = "2018"
[lib]
crate-type = ["cdylib"]
+2 -2
View File
@@ -5,7 +5,7 @@ use rustyline::Editor;
fn main() {
let lua = Lua::new();
let mut editor = Editor::<(), _>::new().expect("Failed to make rustyline editor");
let mut editor = Editor::<()>::new().expect("Failed to make rustyline editor");
loop {
let mut prompt = "> ";
@@ -19,7 +19,7 @@ fn main() {
match lua.load(&line).eval::<MultiValue>() {
Ok(values) => {
editor.add_history_entry(line).unwrap();
editor.add_history_entry(line);
println!(
"{}",
values
+3 -3
View File
@@ -1,8 +1,8 @@
[package]
name = "mlua_derive"
version = "0.9.0-beta.1"
version = "0.8.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
edition = "2018"
description = "Procedural macros for the mlua crate."
repository = "https://github.com/khvzak/mlua"
keywords = ["lua", "mlua"]
@@ -18,7 +18,7 @@ macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error = { version = "1.0", optional = true }
syn = { version = "2.0", features = ["full"] }
syn = { version = "1.0", features = ["full"] }
itertools = { version = "0.10", optional = true }
regex = { version = "1.4", optional = true }
once_cell = { version = "1.0", optional = true }
+23 -45
View File
@@ -1,8 +1,7 @@
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::meta::ParseNestedMeta;
use syn::{parse_macro_input, ItemFn, LitStr, Result};
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
#[cfg(feature = "macros")]
use {
@@ -10,41 +9,19 @@ use {
proc_macro_error::proc_macro_error,
};
#[derive(Default)]
struct ModuleAttributes {
name: Option<Ident>,
}
impl ModuleAttributes {
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
if meta.path.is_ident("name") {
match meta.value() {
Ok(value) => {
self.name = Some(value.parse::<LitStr>()?.parse()?);
}
Err(_) => {
return Err(meta.error("`name` attribute must have a value"));
}
}
} else {
return Err(meta.error("unsupported module attribute"));
}
Ok(())
}
}
#[proc_macro_attribute]
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut args = ModuleAttributes::default();
if !attr.is_empty() {
let args_parser = syn::meta::parser(|meta| args.parse(meta));
parse_macro_input!(attr with args_parser);
let args = parse_macro_input!(attr as AttributeArgs);
let func = parse_macro_input!(item as ItemFn);
if !args.is_empty() {
let err = Error::new(Span::call_site(), "the macro does not support arguments")
.to_compile_error();
return err.into();
}
let func = parse_macro_input!(item as ItemFn);
let func_name = func.sig.ident.clone();
let module_name = args.name.unwrap_or_else(|| func_name.clone());
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
let wrapped = quote! {
::mlua::require_module_feature!();
@@ -87,36 +64,37 @@ pub fn chunk(input: TokenStream) -> TokenStream {
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
use ::std::borrow::Cow;
use ::std::io::Result as IoResult;
use ::std::marker::PhantomData;
use ::std::sync::Mutex;
struct InnerChunk<F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>);
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
impl<F> AsChunk<'static> for InnerChunk<F>
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
where
F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>,
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
{
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
if #caps_len > 0 {
if let Ok(mut make_env) = self.0.lock() {
if let Some(make_env) = make_env.take() {
return make_env(lua);
return make_env(lua).map(Some);
}
}
}
Ok(Value::Nil)
Ok(None)
}
fn mode(&self) -> Option<ChunkMode> {
Some(ChunkMode::Text)
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Borrowed((#source).as_bytes()))
}
}
fn annotate<F: for<'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
let globals = lua.globals();
let env = lua.create_table()?;
@@ -131,7 +109,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
Ok(Value::Table(env))
});
InnerChunk(Mutex::new(Some(make_env)))
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
}};
wrapped_code.into()
+1 -1
View File
@@ -59,7 +59,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
match RE.captures(&format!("{span:?}")) {
match RE.captures(&format!("{:?}", span)) {
Some(caps) => match (caps.get(1), caps.get(2)) {
(Some(start), Some(end)) => Some((
match start.as_str().parse() {
+55 -57
View File
@@ -9,7 +9,7 @@ use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::{FromLuaMulti, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
@@ -18,7 +18,10 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
///
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
/// [`Chunk`]: crate::Chunk
pub trait AsChunk<'a> {
pub trait AsChunk<'lua> {
/// Returns chunk data (can be text or binary)
fn source(&self) -> IoResult<Cow<[u8]>>;
/// Returns optional chunk name
fn name(&self) -> Option<StdString> {
None
@@ -27,73 +30,57 @@ pub trait AsChunk<'a> {
/// Returns optional chunk [environment]
///
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
let _lua = lua; // suppress warning
Ok(Value::Nil)
fn env(&self, _lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
Ok(None)
}
/// Returns optional chunk mode (text or binary)
fn mode(&self) -> Option<ChunkMode> {
None
}
/// Returns chunk data (can be text or binary)
fn source(self) -> IoResult<Cow<'a, [u8]>>;
}
impl<'a> AsChunk<'a> for &'a str {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
impl<'lua> AsChunk<'lua> for str {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
impl AsChunk<'static> for StdString {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self.into_bytes()))
impl<'lua> AsChunk<'lua> for StdString {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
}
}
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] {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
impl<'lua> AsChunk<'lua> for [u8] {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl AsChunk<'static> for Vec<u8> {
fn source(self) -> IoResult<Cow<'static, [u8]>> {
Ok(Cow::Owned(self))
impl<'lua> AsChunk<'lua> for Vec<u8> {
fn source(&self) -> IoResult<Cow<[u8]>> {
Ok(Cow::Borrowed(self))
}
}
impl<'a> AsChunk<'a> for &'a Vec<u8> {
fn source(self) -> IoResult<Cow<'a, [u8]>> {
Ok(Cow::Borrowed(self.as_ref()))
impl<'lua> AsChunk<'lua> for Path {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
}
impl AsChunk<'static> for &Path {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
std::fs::read(self).map(Cow::Owned)
}
}
impl AsChunk<'static> for PathBuf {
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
impl<'lua> AsChunk<'lua> for PathBuf {
fn source(&self) -> IoResult<Cow<[u8]>> {
std::fs::read(self).map(Cow::Owned)
}
fn source(self) -> IoResult<Cow<'static, [u8]>> {
std::fs::read(self).map(Cow::Owned)
fn name(&self) -> Option<StdString> {
Some(format!("@{}", self.display()))
}
}
@@ -103,10 +90,10 @@ impl AsChunk<'static> for PathBuf {
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
pub(crate) lua: &'lua Lua,
pub(crate) name: StdString,
pub(crate) env: Result<Value<'lua>>,
pub(crate) mode: Option<ChunkMode>,
pub(crate) source: IoResult<Cow<'a, [u8]>>,
pub(crate) name: Option<StdString>,
pub(crate) env: Result<Option<Value<'lua>>>,
pub(crate) mode: Option<ChunkMode>,
#[cfg(feature = "luau")]
pub(crate) compiler: Option<Compiler>,
}
@@ -241,6 +228,7 @@ impl Compiler {
coverageLevel: self.coverage_level as c_int,
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
vectorType: ptr::null(),
mutableGlobals: mutable_globals_ptr,
};
ffi::luau_compile(source.as_ref(), options)
@@ -250,9 +238,11 @@ impl Compiler {
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
self.name = Some(name.as_ref().to_string());
// Do extra validation
let _ = self.convert_name()?;
Ok(self)
}
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
@@ -266,9 +256,10 @@ 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 {
self.env = env.into_lua(self.lua);
self
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Self> {
// Prefer to propagate errors here and wrap to `Ok`
self.env = Ok(Some(env.to_lua(self.lua)?));
Ok(self)
}
/// Sets whether the chunk is text or binary (autodetected by default).
@@ -309,7 +300,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// [`exec`]: #method.exec
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn exec_async(self) -> LocalBoxFuture<'lua, Result<()>> {
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
where
'lua: 'fut,
{
self.call_async(())
}
@@ -358,7 +352,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: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
self.into_function()?.call(args)
}
@@ -374,7 +368,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
match self.into_function() {
@@ -394,9 +388,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self.compile();
}
let name = Self::convert_name(self.name)?;
let name = self.convert_name()?;
self.lua
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
}
/// Compiles the chunk and changes mode to binary.
@@ -415,7 +409,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
self.mode = Some(ChunkMode::Binary);
}
#[cfg(not(feature = "luau"))]
if let Ok(func) = self.lua.load_chunk(None, Value::Nil, None, source.as_ref()) {
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
let data = func.dump(false);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
@@ -477,9 +471,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
.map(|c| c.compile(&source))
.unwrap_or(source);
let name = Self::convert_name(self.name.clone())?;
let name = self.convert_name()?;
self.lua
.load_chunk(Some(&name), self.env.clone()?, None, &source)
.load_chunk(&source, name.as_deref(), self.env.clone()?, None)
}
fn detect_mode(&self) -> ChunkMode {
@@ -500,8 +494,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
}
}
fn convert_name(name: String) -> Result<CString> {
CString::new(name).map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
fn convert_name(&self) -> Result<Option<CString>> {
self.name
.clone()
.map(CString::new)
.transpose()
.map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
}
fn expression_source(source: &[u8]) -> Vec<u8> {
+77 -149
View File
@@ -1,3 +1,5 @@
#![allow(clippy::wrong_self_convention)]
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::convert::TryInto;
@@ -15,22 +17,12 @@ use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{LightUserData, MaybeSend};
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
use crate::value::{FromLua, IntoLua, Nil, Value};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
#[cfg(feature = "unstable")]
use crate::{
function::{OwnedFunction, WrappedFunction},
table::OwnedTable,
userdata::OwnedAnyUserData,
};
#[cfg(all(feature = "async", feature = "unstable"))]
use crate::function::WrappedAsyncFunction;
impl<'lua> IntoLua<'lua> for Value<'lua> {
impl<'lua> ToLua<'lua> for Value<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(self)
}
}
@@ -42,9 +34,9 @@ impl<'lua> FromLua<'lua> for Value<'lua> {
}
}
impl<'lua> IntoLua<'lua> for String<'lua> {
impl<'lua> ToLua<'lua> for String<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(self))
}
}
@@ -62,9 +54,9 @@ impl<'lua> FromLua<'lua> for String<'lua> {
}
}
impl<'lua> IntoLua<'lua> for Table<'lua> {
impl<'lua> ToLua<'lua> for Table<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(self))
}
}
@@ -83,25 +75,9 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
}
}
#[cfg(feature = "unstable")]
impl<'lua> IntoLua<'lua> for OwnedTable {
impl<'lua> ToLua<'lua> for Function<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(Table(lua.adopt_owned_ref(self.0))))
}
}
#[cfg(feature = "unstable")]
impl<'lua> FromLua<'lua> for OwnedTable {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedTable> {
Table::from_lua(value, lua).map(|s| s.into_owned())
}
}
impl<'lua> IntoLua<'lua> for Function<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(self))
}
}
@@ -120,41 +96,9 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
}
}
#[cfg(feature = "unstable")]
impl<'lua> IntoLua<'lua> for OwnedFunction {
impl<'lua> ToLua<'lua> for Thread<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(Function(lua.adopt_owned_ref(self.0))))
}
}
#[cfg(feature = "unstable")]
impl<'lua> FromLua<'lua> for OwnedFunction {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedFunction> {
Function::from_lua(value, lua).map(|s| s.into_owned())
}
}
#[cfg(feature = "unstable")]
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
lua.create_callback(self.0).map(Value::Function)
}
}
#[cfg(all(feature = "async", feature = "unstable"))]
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
lua.create_async_callback(self.0).map(Value::Function)
}
}
impl<'lua> IntoLua<'lua> for Thread<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Thread(self))
}
}
@@ -173,9 +117,9 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
}
}
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(self))
}
}
@@ -194,46 +138,30 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
}
#[cfg(feature = "unstable")]
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0))))
}
}
#[cfg(feature = "unstable")]
impl<'lua> FromLua<'lua> for OwnedAnyUserData {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedAnyUserData> {
AnyUserData::from_lua(value, lua).map(|s| s.into_owned())
}
}
impl<'lua, T: 'static + MaybeSend + UserData> IntoLua<'lua> for T {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?))
}
}
impl<'lua, T: 'static> FromLua<'lua> for UserDataRef<'lua, T> {
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Self::from_value(value)
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
message: None,
}),
}
}
}
impl<'lua, T: 'static> FromLua<'lua> for UserDataRefMut<'lua, T> {
impl<'lua> ToLua<'lua> for Error {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Self::from_value(value)
}
}
impl<'lua> IntoLua<'lua> for Error {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Error(self))
}
}
@@ -252,9 +180,9 @@ impl<'lua> FromLua<'lua> for Error {
}
}
impl<'lua> IntoLua<'lua> for bool {
impl<'lua> ToLua<'lua> for bool {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self))
}
}
@@ -270,9 +198,9 @@ impl<'lua> FromLua<'lua> for bool {
}
}
impl<'lua> IntoLua<'lua> for LightUserData {
impl<'lua> ToLua<'lua> for LightUserData {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::LightUserData(self))
}
}
@@ -291,9 +219,9 @@ impl<'lua> FromLua<'lua> for LightUserData {
}
}
impl<'lua> IntoLua<'lua> for StdString {
impl<'lua> ToLua<'lua> for StdString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
@@ -314,23 +242,23 @@ impl<'lua> FromLua<'lua> for StdString {
}
}
impl<'lua> IntoLua<'lua> for &str {
impl<'lua> ToLua<'lua> for &str {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
}
impl<'lua> IntoLua<'lua> for Cow<'_, str> {
impl<'lua> ToLua<'lua> for Cow<'_, str> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> IntoLua<'lua> for Box<str> {
impl<'lua> ToLua<'lua> for Box<str> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&*self)?))
}
}
@@ -352,9 +280,9 @@ impl<'lua> FromLua<'lua> for Box<str> {
}
}
impl<'lua> IntoLua<'lua> for CString {
impl<'lua> ToLua<'lua> for CString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
@@ -382,23 +310,23 @@ impl<'lua> FromLua<'lua> for CString {
}
}
impl<'lua> IntoLua<'lua> for &CStr {
impl<'lua> ToLua<'lua> for &CStr {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> IntoLua<'lua> for Cow<'_, CStr> {
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> IntoLua<'lua> for BString {
impl<'lua> ToLua<'lua> for BString {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
@@ -420,18 +348,18 @@ impl<'lua> FromLua<'lua> for BString {
}
}
impl<'lua> IntoLua<'lua> for &BStr {
impl<'lua> ToLua<'lua> for &BStr {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
}
macro_rules! lua_convert_int {
($x:ty) => {
impl<'lua> IntoLua<'lua> for $x {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.map(Value::Integer)
.or_else(|| cast(self).map(Value::Number))
@@ -492,9 +420,9 @@ lua_convert_int!(usize);
macro_rules! lua_convert_float {
($x:ty) => {
impl<'lua> IntoLua<'lua> for $x {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
@@ -530,24 +458,24 @@ macro_rules! lua_convert_float {
lua_convert_float!(f32);
lua_convert_float!(f64);
impl<'lua, T> IntoLua<'lua> for &[T]
impl<'lua, T> ToLua<'lua> for &[T]
where
T: Clone + IntoLua<'lua>,
T: Clone + ToLua<'lua>,
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
))
}
}
impl<'lua, T, const N: usize> IntoLua<'lua> for [T; N]
impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
where
T: IntoLua<'lua>,
T: ToLua<'lua>,
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
@@ -588,9 +516,9 @@ where
}
}
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Box<[T]> {
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
}
}
@@ -602,9 +530,9 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Vec<T> {
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
@@ -629,11 +557,11 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
}
}
impl<'lua, K: Eq + Hash + IntoLua<'lua>, V: IntoLua<'lua>, S: BuildHasher> IntoLua<'lua>
impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
for HashMap<K, V, S>
{
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
}
@@ -655,9 +583,9 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Defa
}
}
impl<'lua, K: Ord + IntoLua<'lua>, V: IntoLua<'lua>> IntoLua<'lua> for BTreeMap<K, V> {
impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
}
@@ -677,9 +605,9 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
}
}
impl<'lua, T: Eq + Hash + IntoLua<'lua>, S: BuildHasher> IntoLua<'lua> for HashSet<T, S> {
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
)?))
@@ -704,9 +632,9 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
}
}
impl<'lua, T: Ord + IntoLua<'lua>> IntoLua<'lua> for BTreeSet<T> {
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
)?))
@@ -731,11 +659,11 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Option<T> {
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
match self {
Some(val) => val.into_lua(lua),
Some(val) => val.to_lua(lua),
None => Ok(Nil),
}
}
+28 -142
View File
@@ -1,3 +1,5 @@
#![allow(clippy::wrong_self_convention)]
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;
@@ -7,8 +9,6 @@ use std::str::Utf8Error;
use std::string::String as StdString;
use std::sync::Arc;
use crate::private::Sealed;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
#[non_exhaustive]
@@ -71,20 +71,6 @@ pub enum Error {
StackError,
/// Too many arguments to `Function::bind`
BindError,
/// Bad argument received from Lua (usually when calling a function).
///
/// This error can help to identify the argument that caused the error
/// (which is stored in the corresponding field).
BadArgument {
/// Function that was called.
to: Option<StdString>,
/// Argument position (usually starts from 1).
pos: usize,
/// Argument name.
name: Option<StdString>,
/// Underlying error returned when converting argument to a Lua value.
cause: Arc<Error>,
},
/// A Rust value could not be converted to a Lua value.
ToLuaConversionError {
/// Name of the Rust type that could not be converted.
@@ -156,11 +142,8 @@ pub enum Error {
///
/// [`MetaMethod`]: crate::MetaMethod
MetaMethodTypeError {
/// Name of the metamethod.
method: StdString,
/// Passed value type.
type_name: &'static str,
/// A string containing more detailed error information.
message: Option<StdString>,
},
/// A [`RegistryKey`] produced from a different Lua state was used.
@@ -195,13 +178,6 @@ pub enum Error {
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered.
ExternalError(Arc<dyn StdError + Send + Sync>),
/// An error with additional context.
WithContext {
/// A string containing additional context.
context: StdString,
/// Underlying error.
cause: Arc<Error>,
},
}
/// A specialized `Result` type used by `mlua`'s API.
@@ -211,17 +187,17 @@ pub type Result<T> = StdResult<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {msg}"),
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
Error::MemoryError(ref msg) => {
write!(fmt, "memory error: {msg}")
write!(fmt, "memory error: {}", msg)
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {msg}")
write!(fmt, "garbage collector error: {}", msg)
}
Error::SafetyError(ref msg) => {
write!(fmt, "safety error: {msg}")
write!(fmt, "safety error: {}", msg)
},
Error::MemoryLimitNotAvailable => {
write!(fmt, "setting memory limit is not available")
@@ -242,29 +218,18 @@ impl fmt::Display for Error {
fmt,
"too many arguments to Function::bind"
),
Error::BadArgument { ref to, pos, ref name, ref cause } => {
if let Some(name) = name {
write!(fmt, "bad argument `{name}`")?;
} else {
write!(fmt, "bad argument #{pos}")?;
}
if let Some(to) = to {
write!(fmt, " to `{to}`")?;
}
write!(fmt, ": {cause}")
},
Error::ToLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting {from} to Lua {to}")?;
write!(fmt, "error converting {} to Lua {}", from, to)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::FromLuaConversionError { from, to, ref message } => {
write!(fmt, "error converting Lua {from} to {to}")?;
write!(fmt, "error converting Lua {} to {}", from, to)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
@@ -272,25 +237,25 @@ impl fmt::Display for Error {
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
match *message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(ref message) => write!(fmt, " ({})", message),
}
}
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref cause, ref traceback } => {
writeln!(fmt, "callback error")?;
// Trace errors down to the root
let (mut cause, mut full_traceback) = (cause, None);
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
cause = cause2;
full_traceback = Some(traceback2);
}
writeln!(fmt, "{cause}")?;
if let Some(full_traceback) = full_traceback {
let traceback = traceback.trim_start_matches("stack traceback:");
let traceback = traceback.trim_start().trim_end();
@@ -304,24 +269,20 @@ impl fmt::Display for Error {
} else {
writeln!(fmt, "{}", traceback.trim_end())?;
}
Ok(())
write!(fmt, "caused by: {}", cause)
}
Error::PreviouslyResumedPanic => {
write!(fmt, "previously resumed panic returned again")
}
#[cfg(feature = "serialize")]
Error::SerializeError(ref err) => {
write!(fmt, "serialize error: {err}")
write!(fmt, "serialize error: {}", err)
},
#[cfg(feature = "serialize")]
Error::DeserializeError(ref err) => {
write!(fmt, "deserialize error: {err}")
write!(fmt, "deserialize error: {}", err)
},
Error::ExternalError(ref err) => write!(fmt, "{err}"),
Error::WithContext { ref context, ref cause } => {
writeln!(fmt, "{context}")?;
write!(fmt, "{cause}")
}
Error::ExternalError(ref err) => write!(fmt, "{}", err),
}
}
}
@@ -341,122 +302,47 @@ impl StdError for Error {
}
impl Error {
/// Wraps an external error object.
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
Error::ExternalError(err.into().into())
}
/// Attempts to downcast the external error object to a concrete type by reference.
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: StdError + 'static,
{
match self {
Error::ExternalError(err) => err.downcast_ref(),
_ => None,
}
}
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
Error::BadArgument {
to: Some(to.to_string()),
pos: 1,
name: Some("self".to_string()),
cause: Arc::new(cause),
}
}
pub(crate) fn from_lua_conversion<'a>(
from: &'static str,
to: &'static str,
message: impl Into<Option<&'a str>>,
) -> Self {
Error::FromLuaConversionError {
from,
to,
message: message.into().map(|s| s.into()),
}
}
}
pub trait ExternalError {
fn into_lua_err(self) -> Error;
fn to_lua_err(self) -> Error;
}
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
fn into_lua_err(self) -> Error {
fn to_lua_err(self) -> Error {
Error::external(self)
}
}
pub trait ExternalResult<T> {
fn into_lua_err(self) -> Result<T>;
fn to_lua_err(self) -> Result<T>;
}
impl<T, E> ExternalResult<T> for StdResult<T, E>
where
E: ExternalError,
{
fn into_lua_err(self) -> Result<T> {
self.map_err(|e| e.into_lua_err())
fn to_lua_err(self) -> Result<T> {
self.map_err(|e| e.to_lua_err())
}
}
/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
pub trait ErrorContext: Sealed {
/// Wraps the error value with additional context.
fn context<C: fmt::Display>(self, context: C) -> Self;
/// Wrap the error value with additional context that is evaluated lazily
/// only once an error does occur.
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
}
impl ErrorContext for Error {
fn context<C: fmt::Display>(self, context: C) -> Self {
Error::WithContext {
context: context.to_string(),
cause: Arc::new(self),
}
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
Error::WithContext {
context: f(&self).to_string(),
cause: Arc::new(self),
}
}
}
impl<T> ErrorContext for StdResult<T, Error> {
fn context<C: fmt::Display>(self, context: C) -> Self {
self.map_err(|err| Error::WithContext {
context: context.to_string(),
cause: Arc::new(err),
})
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
self.map_err(|err| Error::WithContext {
context: f(&err).to_string(),
cause: Arc::new(err),
})
}
}
impl From<AddrParseError> for Error {
impl std::convert::From<AddrParseError> for Error {
fn from(err: AddrParseError) -> Self {
Error::external(err)
}
}
impl From<IoError> for Error {
impl std::convert::From<IoError> for Error {
fn from(err: IoError) -> Self {
Error::external(err)
}
}
impl From<Utf8Error> for Error {
impl std::convert::From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::external(err)
}
+3
View File
@@ -112,7 +112,10 @@ extern "C" {
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
// Deprecated in Lua 5.4.6
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
#[cfg(feature = "vendored")]
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
+1 -1
View File
@@ -341,7 +341,7 @@ pub unsafe fn luaL_loadbufferx(
fn free(p: *mut c_void);
}
let chunk_is_text = size == 0 || (*data as u8) >= b'\n';
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
if !mode.is_null() {
let modeb = CStr::from_ptr(mode).to_bytes();
if !chunk_is_text && !modeb.contains(&b'b') {
+2 -1
View File
@@ -10,7 +10,8 @@ pub struct lua_CompileOptions {
pub coverageLevel: c_int,
pub vectorLib: *const c_char,
pub vectorCtor: *const c_char,
pub mutableGlobals: *mut *const c_char,
pub vectorType: *const c_char,
pub mutableGlobals: *const *const c_char,
}
extern "C" {
+9 -10
View File
@@ -73,18 +73,17 @@ pub const SYS_MIN_ALIGN: usize = 4;
// by C modules in unsafe mode
#[cfg(not(feature = "luau"))]
pub(crate) fn keep_lua_symbols() {
let mut _symbols: Vec<*const extern "C" fn()> = vec![
lua_atpanic as _,
lua_isuserdata as _,
lua_tocfunction as _,
luaL_loadstring as _,
luaL_openlibs as _,
];
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
symbols.push(lua_atpanic as _);
symbols.push(lua_isuserdata as _);
symbols.push(lua_tocfunction as _);
symbols.push(luaL_loadstring as _);
symbols.push(luaL_openlibs as _);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
_symbols.push(lua_getglobal as _);
_symbols.push(lua_setglobal as _);
_symbols.push(luaL_setfuncs as _);
symbols.push(lua_getglobal as _);
symbols.push(lua_setglobal as _);
symbols.push(luaL_setfuncs as _);
}
}
+32 -139
View File
@@ -5,45 +5,19 @@ use std::slice;
use crate::error::{Error, Result};
use crate::ffi;
use crate::memory::MemoryState;
use crate::types::LuaRef;
use crate::util::{
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLuaMulti};
#[cfg(feature = "unstable")]
use {
crate::lua::Lua,
crate::types::{Callback, MaybeSend},
crate::value::IntoLua,
std::cell::RefCell,
};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
#[cfg(all(feature = "async", feature = "unstable"))]
use {crate::types::AsyncCallback, futures_core::Future, futures_util::TryFutureExt};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua function.
#[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.
pub const fn to_ref(&self) -> Function {
Function(self.0.to_ref())
}
}
#[derive(Clone, Debug)]
pub struct FunctionInfo {
pub name: Option<Vec<u8>>,
@@ -108,34 +82,33 @@ impl<'lua> Function<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua;
let state = lua.state();
let mut args = args.into_lua_multi(lua)?;
let mut args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, nargs + 3)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 3)?;
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
let stack_start = ffi::lua_gettop(state);
ffi::lua_pushcfunction(lua.state, error_traceback);
let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(&self.0);
for arg in args.drain_all() {
lua.push_value(arg)?;
}
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK {
return Err(pop_error(state, ret));
return Err(pop_error(lua.state, ret));
}
let nresults = ffi::lua_gettop(state) - stack_start;
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = args; // Reuse MultiValue container
assert_stack(state, 2);
assert_stack(lua.state, 2);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
ffi::lua_pop(state, 1);
ffi::lua_pop(lua.state, 1);
results
};
R::from_lua_multi(results, lua)
@@ -175,11 +148,11 @@ impl<'lua> Function<'lua> {
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
match lua.create_recycled_thread(self) {
match lua.create_recycled_thread(self.clone()) {
Ok(t) => {
let mut t = t.into_async(args);
t.set_recyclable(true);
@@ -216,7 +189,7 @@ impl<'lua> Function<'lua> {
/// # Ok(())
/// # }
/// ```
pub fn bind<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
@@ -233,9 +206,8 @@ impl<'lua> Function<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
let args = args.into_lua_multi(lua)?;
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
if nargs == 0 {
@@ -247,14 +219,14 @@ impl<'lua> Function<'lua> {
}
let args_wrapper = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, nargs + 3)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 3)?;
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(arg)?;
}
protect_lua!(state, nargs + 1, 1, fn(state) {
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
})?;
@@ -270,7 +242,7 @@ impl<'lua> Function<'lua> {
"#,
)
.try_cache()
.set_name("_mlua_bind")
.set_name("_mlua_bind")?
.call((self.clone(), args_wrapper))
}
@@ -281,17 +253,16 @@ 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 state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
let mut ar: ffi::lua_Debug = mem::zeroed();
lua.push_ref(&self.0);
#[cfg(not(feature = "luau"))]
let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar);
#[cfg(feature = "luau")]
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar);
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
@@ -337,16 +308,15 @@ impl<'lua> Function<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
let mut data: Vec<u8> = Vec::new();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
ffi::lua_dump(state, writer, data_ptr, strip as i32);
ffi::lua_pop(state, 1);
ffi::lua_dump(lua.state, writer, data_ptr, strip as i32);
ffi::lua_pop(lua.state, 1);
}
data
@@ -394,24 +364,15 @@ impl<'lua> Function<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 1);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let func_ptr = &mut func as *mut F as *mut c_void;
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
}
}
/// Convert this handle to owned version.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[inline]
pub fn into_owned(self) -> OwnedFunction {
OwnedFunction(self.0.into_owned())
}
}
impl<'lua> PartialEq for Function<'lua> {
@@ -419,71 +380,3 @@ impl<'lua> PartialEq for Function<'lua> {
self.0 == other.0
}
}
#[cfg(feature = "unstable")]
pub(crate) struct WrappedFunction<'lua>(pub(crate) Callback<'lua, 'static>);
#[cfg(all(feature = "async", feature = "unstable"))]
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
impl<'lua> Function<'lua> {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap<F, A, R>(func: F) -> impl IntoLua<'lua>
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
WrappedFunction(Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua<'lua>
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, args| {
let mut func = func
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}))
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<F, A, FR, R>(func: F) -> impl IntoLua<'lua>
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
{
WrappedAsyncFunction(Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
}))
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Function: Send);
#[cfg(feature = "unstable")]
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
}
+11 -11
View File
@@ -67,12 +67,12 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
@@ -91,12 +91,12 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
"lua_getinfo failed with `S`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0,
"lua_getinfo failed with `s`"
);
@@ -119,12 +119,12 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
@@ -139,7 +139,7 @@ impl<'lua> Debug<'lua> {
pub fn is_tail_call(&self) -> bool {
unsafe {
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0,
"lua_getinfo failed with `t`"
);
(*self.ar.get()).currentline != 0
@@ -151,20 +151,20 @@ impl<'lua> Debug<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
"lua_getinfo failed with `u`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
"lua_getinfo failed with `a`"
);
#[cfg(not(feature = "luau"))]
let stack = DebugStack {
num_ups: (*self.ar.get()).nups as _,
num_ups: (*self.ar.get()).nups as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
num_params: (*self.ar.get()).nparams as _,
num_params: (*self.ar.get()).nparams as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
is_vararg: (*self.ar.get()).isvararg != 0,
};
+8 -42
View File
@@ -10,10 +10,10 @@
//!
//! # Converting data
//!
//! The [`IntoLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
//! The [`ToLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
//! versa. They are implemented for many data structures found in Rust's standard library.
//!
//! For more general conversions, the [`IntoLuaMulti`] and [`FromLuaMulti`] traits allow converting
//! For more general conversions, the [`ToLuaMulti`] and [`FromLuaMulti`] traits allow converting
//! between Rust types and *any number* of Lua values.
//!
//! Most code in `mlua` is generic over implementors of those traits, so in most places the normal
@@ -54,9 +54,9 @@
//! [executing]: crate::Chunk::exec
//! [evaluating]: crate::Chunk::eval
//! [globals]: crate::Lua::globals
//! [`IntoLua`]: crate::IntoLua
//! [`ToLua`]: crate::ToLua
//! [`FromLua`]: crate::FromLua
//! [`IntoLuaMulti`]: crate::IntoLuaMulti
//! [`ToLuaMulti`]: crate::ToLuaMulti
//! [`FromLuaMulti`]: crate::FromLuaMulti
//! [`Function`]: crate::Function
//! [`UserData`]: crate::UserData
@@ -71,8 +71,6 @@
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.9.0-beta.1")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
@@ -90,7 +88,6 @@ mod hook;
mod lua;
#[cfg(feature = "luau")]
mod luau;
mod memory;
mod multi;
mod scope;
mod stdlib;
@@ -99,7 +96,6 @@ mod table;
mod thread;
mod types;
mod userdata;
mod userdata_ext;
mod userdata_impl;
mod util;
mod value;
@@ -109,7 +105,7 @@ pub mod prelude;
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::error::{Error, 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};
@@ -122,11 +118,8 @@ pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
UserDataRef, UserDataRefMut,
};
pub use crate::userdata_ext::AnyUserDataExt;
pub use crate::userdata_impl::UserDataRegistrar;
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
@@ -153,16 +146,12 @@ pub mod serde;
#[macro_use]
extern crate mlua_derive;
// Unstable features
#[cfg(all(feature = "unstable", not(feature = "send")))]
pub use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
///
/// This macro allows to write Lua code directly in Rust code.
///
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
/// User's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
///
/// Captured variables are **moved** into the chunk.
///
@@ -208,7 +197,7 @@ pub use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUs
///
/// [`AsChunk`]: crate::AsChunk
/// [`UserData`]: crate::UserData
/// [`IntoLua`]: crate::IntoLua
/// [`ToLua`]: crate::ToLua
#[cfg(any(feature = "macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::chunk;
@@ -230,29 +219,6 @@ pub use mlua_derive::chunk;
///
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
///
/// You can also pass options to the attribute:
///
/// name - name of the module, defaults to the name of the function
///
/// ```ignore
/// #[mlua::lua_module(name = "alt_module")]
/// fn my_module(lua: &Lua) -> Result<Table> {
/// ...
/// }
/// ```
///
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
pub(crate) mod private {
use super::*;
pub trait Sealed {}
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<'_> {}
}
+664 -667
View File
File diff suppressed because it is too large Load Diff
+6 -8
View File
@@ -1,6 +1,5 @@
use std::ffi::CStr;
use std::os::raw::{c_float, c_int};
use std::string::String as StdString;
use crate::chunk::ChunkMode;
use crate::error::{Error, Result};
@@ -70,15 +69,14 @@ unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
}
}
fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
// Find module in the cache
let state = lua.state();
let loaded = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
protect_lua!(state, 0, 1, fn(state) {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
protect_lua!(lua.state, 0, 1, fn(state) {
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
})?;
Table(lua.pop_ref())
@@ -102,11 +100,11 @@ fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
break;
}
}
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{name}'")))?;
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{}'", name)))?;
let value = lua
.load(&source)
.set_name(&format!("={source_name}"))
.set_name(&format!("={}", source_name))?
.set_mode(ChunkMode::Text)
.call::<_, Value>(())?;
-153
View File
@@ -1,153 +0,0 @@
use std::alloc::{self, Layout};
use std::os::raw::c_void;
use std::ptr;
use crate::ffi;
#[cfg(feature = "luau")]
use crate::lua::ExtraData;
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
#[derive(Default)]
pub(crate) struct MemoryState {
used_memory: isize,
memory_limit: isize,
// Can be set to temporary ignore the memory limit.
// This is used when calling `lua_pushcfunction` for lua5.1/jit/luau.
ignore_limit: bool,
// Indicates that the memory limit was reached on the last allocation.
#[cfg(feature = "luau")]
limit_reached: bool,
}
impl MemoryState {
#[inline]
pub(crate) fn used_memory(&self) -> usize {
self.used_memory as usize
}
#[inline]
pub(crate) fn memory_limit(&self) -> usize {
self.memory_limit as usize
}
#[inline]
pub(crate) fn set_memory_limit(&mut self, limit: usize) -> usize {
let prev_limit = self.memory_limit;
self.memory_limit = limit as isize;
prev_limit as usize
}
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit
// to bypass the memory limit (if set).
#[cfg(any(feature = "lua51", feature = "luajit"))]
#[inline]
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
let mut mem_state: *mut c_void = ptr::null_mut();
if ffi::lua_getallocf(state, &mut mem_state) == ALLOCATOR {
(*(mem_state as *mut MemoryState)).ignore_limit = true;
f();
(*(mem_state as *mut MemoryState)).ignore_limit = false;
} else {
f();
}
}
// Same as the above but for Luau
// It does not have `lua_getallocf` function, so instead we use `lua_callbacks`
#[cfg(feature = "luau")]
#[inline]
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
if extra.is_null() {
return f();
}
let mem_state = (*extra).mem_state();
(*mem_state.as_ptr()).ignore_limit = true;
f();
(*mem_state.as_ptr()).ignore_limit = false;
}
// Does nothing apart from calling `f()`, we don't need to bypass any limits
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
#[inline]
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
f();
}
// Returns `true` if the memory limit was reached on the last memory operation
#[cfg(feature = "luau")]
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
if extra.is_null() {
return false;
}
(*(*extra).mem_state().as_ptr()).limit_reached
}
}
unsafe extern "C" fn allocator(
extra: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void {
let mem_state = &mut *(extra as *mut MemoryState);
#[cfg(feature = "luau")]
{
// Reset the flag
mem_state.limit_reached = false;
}
if nsize == 0 {
// Free memory
if !ptr.is_null() {
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
alloc::dealloc(ptr as *mut u8, layout);
mem_state.used_memory -= osize as isize;
}
return ptr::null_mut();
}
// Do not allocate more than isize::MAX
if nsize > isize::MAX as usize {
return ptr::null_mut();
}
// Are we fit to the memory limits?
let mut mem_diff = nsize as isize;
if !ptr.is_null() {
mem_diff -= osize as isize;
}
let mem_limit = mem_state.memory_limit;
let new_used_memory = mem_state.used_memory + mem_diff;
if mem_limit > 0 && new_used_memory > mem_limit && !mem_state.ignore_limit {
#[cfg(feature = "luau")]
{
mem_state.limit_reached = true;
}
return ptr::null_mut();
}
mem_state.used_memory += mem_diff;
if ptr.is_null() {
// Allocate new memory
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
if new_ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
return new_ptr;
}
// Reallocate memory
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
if new_ptr.is_null() {
alloc::handle_alloc_error(old_layout);
}
new_ptr
}
+32 -54
View File
@@ -1,21 +1,23 @@
#![allow(clippy::wrong_self_convention)]
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult;
use crate::error::Result;
use crate::lua::Lua;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::new_or_pooled(lua);
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::new_or_cached(lua);
match self {
Ok(v) => result.push_front(v.into_lua(lua)?),
Ok(v) => result.push_front(v.to_lua(lua)?),
Err(e) => {
result.push_front(e.into_lua(lua)?);
result.push_front(e.to_lua(lua)?);
result.push_front(Nil);
}
}
@@ -23,11 +25,11 @@ impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new_or_pooled(lua);
v.push_front(self.into_lua(lua)?);
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new_or_cached(lua);
v.push_front(self.to_lua(lua)?);
Ok(v)
}
}
@@ -36,26 +38,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
MultiValue::return_to_pool(values, lua);
res
}
#[inline]
fn from_lua_multi_args(
mut values: MultiValue<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
let res = T::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua);
MultiValue::return_to_pool(values, lua);
lua.cache_multivalue(values);
res
}
}
impl<'lua> IntoLuaMulti<'lua> for MultiValue<'lua> {
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
#[inline]
fn into_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(self)
}
}
@@ -138,11 +128,11 @@ impl<T> DerefMut for Variadic<T> {
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut values = MultiValue::new_or_pooled(lua);
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut values = MultiValue::new_or_cached(lua);
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
Ok(values)
}
}
@@ -155,42 +145,42 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
.map(|e| T::from_lua(e, lua))
.collect::<Result<Vec<T>>>()
.map(Variadic);
MultiValue::return_to_pool(values, lua);
lua.cache_multivalue(values);
res
}
}
macro_rules! impl_tuple {
() => (
impl<'lua> IntoLuaMulti<'lua> for () {
impl<'lua> ToLuaMulti<'lua> for () {
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_pooled(lua))
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_cached(lua))
}
}
impl<'lua> FromLuaMulti<'lua> for () {
#[inline]
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
MultiValue::return_to_pool(values, lua);
lua.cache_multivalue(values);
Ok(())
}
}
);
($last:ident $($name:ident)*) => (
impl<'lua, $($name,)* $last> IntoLuaMulti<'lua> for ($($name,)* $last,)
where $($name: IntoLua<'lua>,)*
$last: IntoLuaMulti<'lua>
impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,)
where $($name: ToLua<'lua>,)*
$last: ToLuaMulti<'lua>
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let ($($name,)* $last,) = self;
let mut results = $last.into_lua_multi(lua)?;
push_reverse!(results, $($name.into_lua(lua)?,)*);
let mut results = $last.to_lua_multi(lua)?;
push_reverse!(results, $($name.to_lua(lua)?,)*);
Ok(results)
}
}
@@ -203,21 +193,9 @@ macro_rules! impl_tuple {
#[allow(non_snake_case)]
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
$(let $name = values.pop_front().unwrap_or(Nil);)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
Ok(($($name,)* $last,))
}
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn from_lua_multi_args(mut values: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
$(
let $name = FromLua::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua)?;
i += 1;
)*
let $last = FromLuaMulti::from_lua_multi_args(values, i, to, lua)?;
Ok(($($name,)* $last,))
Ok(($(FromLua::from_lua($name, lua)?,)* $last,))
}
}
);
+10 -19
View File
@@ -2,18 +2,16 @@
#[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,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistrar as LuaUserDataRegistrar,
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, LuaOptions,
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs,
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua,
ToLuaMulti, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
Value as LuaValue,
};
@@ -35,10 +33,3 @@ pub use crate::{
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
SerializeOptions as LuaSerializeOptions,
};
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[doc(no_inline)]
pub use crate::{
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
OwnedTable as LuaOwnedTable,
};
+368 -290
View File
@@ -2,7 +2,8 @@ use std::any::Any;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_int;
use std::os::raw::{c_int, c_void};
use std::rc::Rc;
#[cfg(feature = "serialize")]
use serde::Serialize;
@@ -19,13 +20,17 @@ use crate::util::{
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
take_userdata, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
#[cfg(feature = "lua54")]
use crate::userdata::USER_VALUE_MAXSLOT;
#[cfg(feature = "async")]
use futures_core::future::Future;
use {
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
futures_core::future::Future,
futures_util::future::{self, TryFutureExt},
};
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
/// callbacks that are not required to be Send or 'static.
@@ -60,8 +65,8 @@ 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>,
F: Fn(&'callback Lua, A) -> Result<R> + 'scope,
R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
{
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
// callback itself must be 'scope lifetime, so the function should not be able to capture
@@ -74,7 +79,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// scope, and owned inside the callback itself.
unsafe {
self.create_callback(Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}))
}
}
@@ -93,8 +98,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: IntoLuaMulti<'callback>,
F: FnMut(&'callback Lua, A) -> Result<R> + 'scope,
R: ToLuaMulti<'callback>,
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
@@ -104,7 +109,40 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
})
}
/// Creates a Lua userdata object from a custom userdata type.
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
///
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
///
/// Requires `feature = "async"`
///
/// [`Lua::create_async_function`]: crate::Lua::create_async_function
/// [`Lua::scope`]: crate::Lua::scope
/// [`Lua::async_scope`]: crate::Lua::async_scope
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn create_async_function<'callback, A, R, F, FR>(
&'callback self,
func: F,
) -> Result<Function<'lua>>
where
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'callback Lua, A) -> FR,
FR: 'callback + Future<Output = Result<R>>,
{
unsafe {
self.create_async_callback(Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
}))
}
}
/// Create a Lua userdata object from a custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the
@@ -115,18 +153,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// [`Lua::scope`]: crate::Lua::scope
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: UserData + 'static,
T: 'static + UserData,
{
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
// thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe {
let ud = self.lua.make_userdata(UserDataCell::new(data))?;
self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
self.create_userdata_inner(UserDataCell::new(data))
}
/// Creates a Lua userdata object from a custom serializable userdata type.
/// Create a Lua userdata object from a custom serializable userdata type.
///
/// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the
@@ -141,125 +173,60 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: UserData + Serialize + 'static,
T: 'static + UserData + Serialize,
{
unsafe {
let ud = self.lua.make_userdata(UserDataCell::new_ser(data))?;
self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
self.create_userdata_inner(UserDataCell::new_ser(data))
}
/// Creates a Lua userdata object from a reference to custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send. This method takes non-'static
/// reference to the data. See [`Lua::scope`] for more details.
///
/// Userdata created with this method will not be able to be mutated from Lua.
pub fn create_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
where
T: UserData + 'static,
T: 'static + UserData,
{
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
// thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe {
let ud = self.lua.make_userdata(UserDataCell::new_ref(data))?;
self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
}
let ud = self.lua.make_userdata(data)?;
/// Creates a Lua userdata object from a mutable reference to custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send. This method takes non-'static
/// mutable reference to the data. See [`Lua::scope`] for more details.
pub fn create_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
where
T: UserData + 'static,
{
unsafe {
let ud = self.lua.make_userdata(UserDataCell::new_ref_mut(data))?;
self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
}
/// Creates a Lua userdata object from a reference to custom Rust type.
///
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the Rust type be Send. This method takes non-'static
/// reference to the data. See [`Lua::scope`] for more details.
///
/// Userdata created with this method will not be able to be mutated from Lua.
pub fn create_any_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
where
T: 'static,
{
unsafe {
let ud = self.lua.make_any_userdata(UserDataCell::new_ref(data))?;
self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
}
/// Creates a Lua userdata object from a mutable reference to custom Rust type.
///
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the Rust type be Send. This method takes non-'static
/// mutable reference to the data. See [`Lua::scope`] for more details.
pub fn create_any_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
where
T: 'static,
{
let lua = self.lua;
unsafe {
let ud = lua.make_any_userdata(UserDataCell::new_ref_mut(data))?;
self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
}
/// Shortens the lifetime of a userdata to the lifetime of the scope.
unsafe fn seal_userdata<T: 'static>(&self, ud: &AnyUserData<'lua>) -> Result<()> {
#[cfg(any(feature = "lua51", feature = "luajit"))]
let newtable = self.lua.create_table()?;
let destructor: DestructorCallback = Box::new(move |ud| {
let state = ud.lua.state();
let _sg = StackGuard::new(state);
assert_stack(state, 2);
// Check that userdata is not destructed (via `take()` call)
if ud.lua.push_userdata_ref(&ud).is_err() {
return vec![];
}
// Clear associated user values
#[cfg(feature = "lua54")]
for i in 1..=USER_VALUE_MAXSLOT {
ffi::lua_pushnil(state);
ffi::lua_setiuservalue(state, -2, i as c_int);
}
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
{
ffi::lua_pushnil(state);
ffi::lua_setuservalue(state, -2);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
{
ud.lua.push_ref(&newtable.0);
ffi::lua_setuservalue(state, -2);
}
let newtable = self.lua.create_table()?;
let destructor: DestructorCallback = Box::new(move |ud| {
let state = ud.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
});
self.destructors
.borrow_mut()
.push((ud.0.clone(), destructor));
// Check that userdata is not destructed (via `take()` call)
if ud.lua.push_userdata_ref(&ud).is_err() {
return vec![];
}
Ok(())
// Clear associated user values
#[cfg(feature = "lua54")]
for i in 1..=USER_VALUE_MAXSLOT {
ffi::lua_pushnil(state);
ffi::lua_setiuservalue(state, -2, i as c_int);
}
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
{
ffi::lua_pushnil(state);
ffi::lua_setuservalue(state, -2);
}
#[cfg(any(feature = "lua51", feature = "luajit"))]
{
ud.lua.push_ref(&newtable.0);
ffi::lua_setuservalue(state, -2);
}
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
});
self.destructors
.borrow_mut()
.push((ud.0.clone(), destructor));
Ok(ud)
}
}
/// Creates a Lua userdata object from a custom userdata type.
/// Create a Lua userdata object from a custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send or 'static. See
@@ -284,8 +251,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// [`UserDataMethods`]: crate::UserDataMethods
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
T: UserData + 'scope,
T: 'scope + UserData,
{
let data = Rc::new(RefCell::new(data));
// 'callback outliving 'scope is a lie to make the types work out, required due to the
// inability to work with the more correct callback type that is universally quantified over
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
@@ -293,7 +262,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &Scope<'lua, 'scope>,
ud_ptr: *const UserDataCell<T>,
data: Rc<RefCell<T>>,
ud_ptr: *const c_void,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in
@@ -303,15 +273,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// with a type mismatch, but here without this check would proceed as though you had
// called the method on the original value (since we otherwise completely ignore the
// first argument).
let check_ud_type = move |lua: &Lua, value| -> Result<&UserDataCell<T>> {
let check_ud_type = move |lua: &'callback Lua, value| {
if let Some(Value::UserData(ud)) = value {
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
lua.push_userdata_ref(&ud.0)?;
if get_userdata(state, -1) as *const _ == ud_ptr {
return Ok(&*ud_ptr);
if get_userdata(lua.state, -1) as *const _ == ud_ptr {
return Ok(());
}
}
};
@@ -321,8 +290,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
match method {
NonStaticMethod::Method(method) => {
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
let data = check_ud_type(lua, args.pop_front())?;
let data = data.try_borrow()?;
check_ud_type(lua, args.pop_front())?;
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &*data, args)
});
unsafe { scope.create_callback(f) }
@@ -330,11 +299,13 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
NonStaticMethod::MethodMut(method) => {
let method = RefCell::new(method);
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
let data = check_ud_type(lua, args.pop_front())?;
check_ud_type(lua, args.pop_front())?;
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let mut data = data.try_borrow_mut()?;
let mut data = data
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
(*method)(lua, &mut *data, args)
});
unsafe { scope.create_callback(f) }
@@ -359,16 +330,16 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
T::add_fields(&mut ud_fields);
T::add_methods(&mut ud_methods);
let lua = self.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 13)?;
let lua = self.lua;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 13)?;
#[cfg(not(feature = "luau"))]
#[allow(clippy::let_and_return)]
let ud_ptr = protect_lua!(state, 0, 1, |state| {
let ud = ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<T>>());
let ud_ptr = protect_lua!(lua.state, 0, 1, |state| {
let ud =
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>());
// Set empty environment for Lua 5.1
#[cfg(any(feature = "lua51", feature = "luajit"))]
@@ -377,64 +348,72 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
ffi::lua_setuservalue(state, -2);
}
ud as *const UserDataCell<T>
ud
})?;
#[cfg(feature = "luau")]
let ud_ptr = {
crate::util::push_userdata(state, UserDataCell::new(data), true)?;
ffi::lua_touserdata(state, -1) as *const UserDataCell<T>
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
UserDataCell::new(data.clone()),
true,
)?;
ffi::lua_touserdata(lua.state, -1)
};
// Prepare metatable, add meta methods first and then meta fields
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
push_table(state, 0, meta_methods_nrec as c_int, true)?;
push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
for (k, m) in ud_methods.meta_methods {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, k.validate()?.name())?;
}
for (k, f) in ud_fields.meta_fields {
lua.push_value(f(mem::transmute(lua))?)?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
rawset_field(lua.state, -2, k.validate()?.name())?;
}
let metatable_index = ffi::lua_absindex(state, -1);
let metatable_index = ffi::lua_absindex(lua.state, -1);
let mut field_getters_index = None;
let field_getters_nrec = ud_fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec as c_int, true)?;
push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_getters {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, &k)?;
}
field_getters_index = Some(ffi::lua_absindex(state, -1));
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
}
let mut field_setters_index = None;
let field_setters_nrec = ud_fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(state, 0, field_setters_nrec as c_int, true)?;
push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_setters {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, &k)?;
}
field_setters_index = Some(ffi::lua_absindex(state, -1));
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
}
let mut methods_index = None;
let methods_nrec = ud_methods.methods.len();
if methods_nrec > 0 {
// Create table used for methods lookup
push_table(state, 0, methods_nrec as c_int, true)?;
push_table(lua.state, 0, methods_nrec as c_int, true)?;
for (k, m) in ud_methods.methods {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
rawset_field(lua.state, -2, &k)?;
}
methods_index = Some(ffi::lua_absindex(state, -1));
methods_index = Some(ffi::lua_absindex(lua.state, -1));
}
init_userdata_metatable::<UserDataCell<T>>(
state,
init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
metatable_index,
field_getters_index,
field_setters_index,
@@ -444,20 +423,20 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let count = field_getters_index.map(|_| 1).unwrap_or(0)
+ field_setters_index.map(|_| 1).unwrap_or(0)
+ methods_index.map(|_| 1).unwrap_or(0);
ffi::lua_pop(state, count);
ffi::lua_pop(lua.state, count);
let mt_ptr = ffi::lua_topointer(state, -1);
let mt_ptr = ffi::lua_topointer(lua.state, -1);
// Write userdata just before attaching metatable with `__gc` metamethod
#[cfg(not(feature = "luau"))]
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
ffi::lua_setmetatable(state, -2);
ffi::lua_setmetatable(lua.state, -2);
let ud = AnyUserData(lua.pop_ref());
lua.register_raw_userdata_metatable(mt_ptr, None);
lua.register_userdata_metatable(mt_ptr, None);
#[cfg(any(feature = "lua51", feature = "luajit"))]
let newtable = lua.create_table()?;
let destructor: DestructorCallback = Box::new(move |ud| {
let state = ud.lua.state();
let state = ud.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
@@ -470,7 +449,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
ffi::lua_getmetatable(state, -1);
let mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ud.lua.deregister_raw_userdata_metatable(mt_ptr);
ud.lua.deregister_userdata_metatable(mt_ptr);
// Clear associated user values
#[cfg(feature = "lua54")]
@@ -495,8 +474,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
mem::transmute(f)
}
let ud = take_userdata::<UserDataCell<T>>(state);
vec![Box::new(seal(ud))]
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
vec![ud]
});
self.destructors
.borrow_mut()
@@ -519,7 +498,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let f = self.lua.create_callback(f)?;
let destructor: DestructorCallback = Box::new(|f| {
let state = f.lua.state();
let state = f.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 3);
@@ -540,6 +519,64 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
Ok(f)
}
#[cfg(feature = "async")]
unsafe fn create_async_callback<'callback>(
&self,
f: AsyncCallback<'callback, 'scope>,
) -> Result<Function<'lua>> {
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
let f = self.lua.create_async_callback(f)?;
// We need to pre-allocate strings to avoid failures in destructor.
let get_poll_str = self.lua.create_string("get_poll")?;
let poll_str = self.lua.create_string("poll")?;
let destructor: DestructorCallback = Box::new(move |f| {
let state = f.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 5);
f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback.
// First, get the environment table
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_getupvalue(state, -1, 1);
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
ffi::lua_getfenv(state, -1);
// Second, get the `get_poll()` closure using the corresponding key
f.lua.push_ref(&get_poll_str.0);
ffi::lua_rawget(state, -2);
// Destroy all upvalues
ffi::lua_getupvalue(state, -1, 1);
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1);
ffi::lua_pop(state, 1);
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
// Finally, get polled future and destroy it
f.lua.push_ref(&poll_str.0);
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
ffi::lua_getupvalue(state, -1, 1);
let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1);
data.push(Box::new(upvalue2));
}
data
});
self.destructors
.borrow_mut()
.push((f.0.clone(), destructor));
Ok(f)
}
}
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
@@ -569,8 +606,8 @@ enum NonStaticMethod<'lua, T> {
}
struct NonStaticUserDataMethods<'lua, T: UserData> {
methods: Vec<(String, NonStaticMethod<'lua, T>)>,
meta_methods: Vec<(String, NonStaticMethod<'lua, T>)>,
methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
}
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
@@ -583,162 +620,190 @@ impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
}
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.methods.push((name.as_ref().into(), method));
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, mut method: M)
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.methods.push((name.as_ref().into(), method));
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous methods are not supported for non-static userdata")
mlua_panic!("asynchronous methods are not supported for non-static userdata")
}
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.methods.push((name.as_ref().into(), func));
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, mut function: F)
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.methods.push((name.as_ref().into(), func));
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[cfg(feature = "async")]
fn add_async_function<F, A, FR, R>(&mut self, _name: impl AsRef<str>, _function: F)
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous functions are not supported for non-static userdata")
mlua_panic!("asynchronous functions are not supported for non-static userdata")
}
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.meta_methods.push((name.as_ref().into(), method));
self.meta_methods.push((
meta.into(),
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, mut method: M)
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, mut method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.meta_methods.push((name.as_ref().into(), method));
self.meta_methods.push((
meta.into(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
fn add_async_meta_method<S, A, R, M, MR>(&mut self, _meta: S, _method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous meta methods are not supported for non-static userdata")
mlua_panic!("asynchronous meta methods are not supported for non-static userdata")
}
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.meta_methods.push((name.as_ref().into(), func));
self.meta_methods.push((
meta.into(),
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, mut function: F)
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, mut function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.meta_methods.push((name.as_ref().into(), func));
self.meta_methods.push((
meta.into(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
#[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)
fn add_async_meta_function<S, A, R, F, FR>(&mut self, _meta: S, _function: F)
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
// The panic should never happen as async non-static code wouldn't compile
// Non-static lifetime must be bounded to 'lua lifetime
panic!("asynchronous meta functions are not supported for non-static userdata")
mlua_panic!("asynchronous meta functions are not supported for non-static userdata")
}
}
struct NonStaticUserDataFields<'lua, T: UserData> {
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
field_getters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
field_setters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
#[allow(clippy::type_complexity)]
meta_fields: Vec<(String, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
meta_fields: Vec<(MetaMethod, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
}
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
@@ -752,67 +817,80 @@ impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
}
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
where
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
{
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| {
method(lua, ud)?.into_lua_multi(lua)
}));
self.field_getters.push((name.as_ref().into(), method));
self.field_getters.push((
name.as_ref().to_vec(),
NonStaticMethod::Method(Box::new(move |lua, ud, _| {
method(lua, ud)?.to_lua_multi(lua)
})),
));
}
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
fn add_field_method_set<S, A, M>(&mut self, name: &S, mut method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
{
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.field_setters.push((name.as_ref().into(), method));
self.field_setters.push((
name.as_ref().to_vec(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
where
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
{
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
}));
self.field_getters.push((name.as_ref().into(), func));
self.field_getters.push((
name.as_ref().to_vec(),
NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
));
}
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
where
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
{
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
let (ud, val) = <_>::from_lua_multi(args, lua)?;
function(lua, ud, val)?.into_lua_multi(lua)
}));
self.field_setters.push((name.as_ref().into(), func));
self.field_setters.push((
name.as_ref().to_vec(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
let (ud, val) = <_>::from_lua_multi(args, lua)?;
function(lua, ud, val)?.to_lua_multi(lua)
})),
));
}
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
where
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
S: Into<MetaMethod>,
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
R: ToLua<'lua>,
{
let name = name.as_ref().to_string();
let meta = meta.into();
self.meta_fields.push((
name.clone(),
meta.clone(),
Box::new(move |lua| {
let value = f(lua)?.into_lua(lua)?;
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
let value = f(lua)?.to_lua(lua)?;
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
match value {
Value::Nil | Value::Table(_) | Value::Function(_) => {}
_ => {
return Err(Error::MetaMethodTypeError {
method: name.clone(),
method: meta.to_string(),
type_name: value.type_name(),
message: Some("expected nil, table or function".to_string()),
})
+25 -30
View File
@@ -9,7 +9,6 @@ use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result};
use crate::table::{Table, TablePairs, TableSequence};
use crate::userdata::AnyUserData;
use crate::value::Value;
/// A struct for deserializing Lua values into Rust values.
@@ -132,9 +131,6 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
Value::Table(_) => self.deserialize_map(visitor),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_any(visitor))
}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
@@ -167,8 +163,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
#[inline]
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
_name: &str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
@@ -202,9 +198,6 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
(variant, Some(value), Some(_guard))
}
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
Value::UserData(ud) if ud.is_serializable() => {
return serde_userdata(ud, |value| value.deserialize_enum(name, variants, visitor));
}
_ => return Err(de::Error::custom("bad enum value")),
};
@@ -251,9 +244,6 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
))
}
}
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_seq(visitor))
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
@@ -309,9 +299,6 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
))
}
}
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_map(visitor))
}
value => Err(de::Error::invalid_type(
de::Unexpected::Other(value.type_name()),
&"table",
@@ -333,21 +320,38 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
}
#[inline]
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
#[inline]
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_newtype_struct(name, visitor))
}
_ => visitor.visit_newtype_struct(self),
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
#[inline]
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf unit unit_struct identifier ignored_any
byte_buf identifier ignored_any
}
}
@@ -608,7 +612,6 @@ fn check_value_if_skip(
return Ok(true); // skip
}
}
Value::UserData(ud) if ud.is_serializable() => {}
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
@@ -622,11 +625,3 @@ fn check_value_if_skip(
}
Ok(false) // do not skip
}
fn serde_userdata<V>(
ud: AnyUserData,
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
) -> Result<V> {
let value = serde_value::to_value(ud).map_err(|err| Error::SerializeError(err.to_string()))?;
f(value).map_err(|err| Error::DeserializeError(err.to_string()))
}
+1 -2
View File
@@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::private::Sealed;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::check_stack;
@@ -16,7 +15,7 @@ use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub trait LuaSerdeExt<'lua>: Sealed {
pub trait LuaSerdeExt<'lua> {
/// A special value (lightuserdata) to encode/decode optional (none) values.
///
/// Requires `feature = "serialize"`
+8 -9
View File
@@ -10,7 +10,7 @@ use crate::string::String;
use crate::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, StackGuard};
use crate::value::{IntoLua, Value};
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
@@ -110,7 +110,7 @@ macro_rules! lua_serialize_number {
($name:ident, $t:ty) => {
#[inline]
fn $name(self, value: $t) -> Result<Value<'lua>> {
value.into_lua(self.lua)
value.to_lua(self.lua)
}
};
}
@@ -320,21 +320,20 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let state = lua.state();
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
ffi::lua_pop(state, 1);
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
ffi::lua_rawseti(lua.state, -2, len + 1);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(state, 2, 0, fn(state) {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
+3 -39
View File
@@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow};
use std::hash::{Hash, Hasher};
use std::os::raw::c_void;
use std::string::String as StdString;
use std::{fmt, slice, str};
use std::{slice, str};
#[cfg(feature = "serialize")]
use {
@@ -17,7 +17,7 @@ use crate::types::LuaRef;
/// Handle to an internal Lua string.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
impl<'lua> String<'lua> {
@@ -124,35 +124,6 @@ impl<'lua> String<'lua> {
}
}
impl<'lua> fmt::Debug for String<'lua> {
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) {
return s.fmt(f);
}
// Format as bytes
write!(f, "b\"")?;
for &b in bytes {
// https://doc.rust-lang.org/reference/tokens.html#byte-escapes
match b {
b'\n' => write!(f, "\\n")?,
b'\r' => write!(f, "\\r")?,
b'\t' => write!(f, "\\t")?,
b'\\' | b'"' => write!(f, "\\{}", b as char)?,
b'\0' => write!(f, "\\0")?,
// ASCII printable
0x20..=0x7e => write!(f, "{}", b as char)?,
_ => write!(f, "\\x{b:02x}")?,
}
}
write!(f, "\"")?;
Ok(())
}
}
impl<'lua> AsRef<[u8]> for String<'lua> {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
@@ -175,7 +146,7 @@ impl<'lua> Borrow<[u8]> for String<'lua> {
// in other ways.
impl<'lua, T> PartialEq<T> for String<'lua>
where
T: AsRef<[u8]> + ?Sized,
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
self.as_bytes() == other.as_ref()
@@ -202,10 +173,3 @@ impl<'lua> Serialize for String<'lua> {
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(String: Send);
}
+101 -192
View File
@@ -11,10 +11,9 @@ use {
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::private::Sealed;
use crate::types::{Integer, LuaRef};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
@@ -23,20 +22,6 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
#[derive(Clone, Debug)]
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
/// Owned handle to an internal Lua table.
#[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.
pub const fn to_ref(&self) -> Table {
Table(self.0.to_ref())
}
}
#[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> {
/// Sets a key-value pair in the table.
@@ -72,25 +57,24 @@ 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: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
// Fast track
if !self.has_metatable() {
return self.raw_set(key, value);
}
let lua = self.0.lua;
let key = key.into_lua(lua)?;
let value = value.into_lua(lua)?;
let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
}
}
@@ -118,23 +102,22 @@ 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: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
// Fast track
if !self.has_metatable() {
return self.raw_get(key);
}
let lua = self.0.lua;
let state = lua.state();
let key = key.into_lua(lua)?;
let key = key.to_lua(lua)?;
let value = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
lua.pop_value()
};
@@ -142,27 +125,26 @@ impl<'lua> Table<'lua> {
}
/// Checks whether the table contains a non-nil value for `key`.
pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
Ok(self.get::<_, Value>(key)? != Value::Nil)
}
/// Appends a value to the back of the table.
pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
pub fn push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
// Fast track
if !self.has_metatable() {
return self.raw_push(value);
}
let lua = self.0.lua;
let state = lua.state();
let value = value.into_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
protect_lua!(state, 2, 0, fn(state) {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::luaL_len(state, -2) as Integer;
ffi::lua_seti(state, -2, len + 1);
})?
@@ -178,13 +160,12 @@ impl<'lua> Table<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
let value = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua!(state, 1, 1, fn(state) {
protect_lua!(lua.state, 1, 1, fn(state) {
let len = ffi::luaL_len(state, -1) as Integer;
ffi::lua_geti(state, -1, len);
ffi::lua_pushnil(state);
@@ -252,46 +233,44 @@ 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: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let state = lua.state();
let key = key.into_lua(lua)?;
let value = value.into_lua(lua)?;
let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
if lua.unlikely_memory_error() {
ffi::lua_rawset(state, -3);
ffi::lua_pop(state, 1);
ffi::lua_rawset(lua.state, -3);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
}
}
}
/// Gets the value associated to `key` without invoking metamethods.
pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua;
let state = lua.state();
let key = key.into_lua(lua)?;
let key = key.to_lua(lua)?;
let value = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
ffi::lua_rawget(state, -2);
ffi::lua_rawget(lua.state, -2);
lua.pop_value()
};
@@ -300,23 +279,21 @@ impl<'lua> Table<'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<()> {
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
let size = self.raw_len();
if idx < 1 || idx > size + 1 {
return Err(Error::RuntimeError("index out of bounds".to_string()));
}
let value = value.into_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
protect_lua!(state, 2, 0, |state| {
protect_lua!(lua.state, 2, 0, |state| {
for i in (idx..=size).rev() {
// table[i+1] = table[i]
ffi::lua_rawgeti(state, -2, i);
@@ -328,17 +305,16 @@ 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: ToLua<'lua>>(&self, value: V) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
let state = lua.state();
let value = value.into_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
@@ -349,9 +325,9 @@ impl<'lua> Table<'lua> {
}
if lua.unlikely_memory_error() {
callback(state);
callback(lua.state);
} else {
protect_lua!(state, 2, 0, fn(state) callback(state))?;
protect_lua!(lua.state, 2, 0, fn(state) callback(state))?;
}
}
Ok(())
@@ -363,17 +339,16 @@ impl<'lua> Table<'lua> {
self.check_readonly_write()?;
let lua = self.0.lua;
let state = lua.state();
let value = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_ref(&self.0);
let len = ffi::lua_rawlen(state, -1) as Integer;
ffi::lua_rawgeti(state, -1, len);
let len = ffi::lua_rawlen(lua.state, -1) as Integer;
ffi::lua_rawgeti(lua.state, -1, len);
// Set slot to nil (it must be safe to do)
ffi::lua_pushnil(state);
ffi::lua_rawseti(state, -3, len);
ffi::lua_pushnil(lua.state);
ffi::lua_rawseti(lua.state, -3, len);
lua.pop_value()
};
V::from_lua(value, lua)
@@ -386,10 +361,9 @@ 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<()> {
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
let key = key.into_lua(lua)?;
let key = key.to_lua(lua)?;
match key {
Value::Integer(idx) => {
let size = self.raw_len();
@@ -397,11 +371,11 @@ impl<'lua> Table<'lua> {
return Err(Error::RuntimeError("index out of bounds".to_string()));
}
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua!(state, 1, 0, |state| {
protect_lua!(lua.state, 1, 0, |state| {
for i in idx..size {
ffi::lua_rawgeti(state, -1, i + 1);
ffi::lua_rawseti(state, -2, i);
@@ -415,47 +389,6 @@ impl<'lua> Table<'lua> {
}
}
/// Clears the table, removing all keys and values from array and hash parts,
/// without invoking metamethods.
///
/// This method is useful to clear the table while keeping its capacity.
pub fn clear(&self) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
unsafe {
#[cfg(feature = "luau")]
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
#[cfg(not(feature = "luau"))]
{
let state = lua.state();
check_stack(state, 4)?;
lua.push_ref(&self.0);
// Clear array part
for i in 1..=ffi::lua_rawlen(state, -1) {
ffi::lua_pushnil(state);
ffi::lua_rawseti(state, -2, i as Integer);
}
// Clear hash part
// It must be safe as long as we don't use invalid keys
ffi::lua_pushnil(state);
while ffi::lua_next(state, -2) != 0 {
ffi::lua_pop(state, 1); // pop value
ffi::lua_pushvalue(state, -1); // copy key
ffi::lua_pushnil(state);
ffi::lua_rawset(state, -4);
}
}
}
Ok(())
}
/// Returns the result of the Lua `#` operator.
///
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
@@ -468,13 +401,12 @@ impl<'lua> Table<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1))
protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
}
}
@@ -489,13 +421,12 @@ impl<'lua> Table<'lua> {
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
pub fn get_metatable(&self) -> Option<Table<'lua>> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(&self.0);
if ffi::lua_getmetatable(state, -1) == 0 {
if ffi::lua_getmetatable(lua.state, -1) == 0 {
None
} else {
Some(Table(lua.pop_ref()))
@@ -515,18 +446,17 @@ impl<'lua> Table<'lua> {
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(&self.0);
if let Some(metatable) = metatable {
lua.push_ref(&metatable.0);
} else {
ffi::lua_pushnil(state);
ffi::lua_pushnil(lua.state);
}
ffi::lua_setmetatable(state, -2);
ffi::lua_setmetatable(lua.state, -2);
}
}
@@ -582,14 +512,6 @@ impl<'lua> Table<'lua> {
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
}
/// Convert this handle to owned version.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[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.
///
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
@@ -715,17 +637,16 @@ impl<'lua> Table<'lua> {
#[cfg(feature = "serialize")]
pub(crate) fn is_array(&self) -> bool {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 3);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
lua.push_ref(&self.0);
if ffi::lua_getmetatable(state, -1) == 0 {
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return false;
}
crate::serde::push_array_metatable(state);
ffi::lua_rawequal(state, -1, -2) != 0
crate::serde::push_array_metatable(lua.state);
ffi::lua_rawequal(lua.state, -1, -2) != 0
}
}
@@ -754,13 +675,13 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
}
/// An extension trait for `Table`s that provides a variety of convenient functionality.
pub trait TableExt<'lua>: Sealed {
pub trait TableExt<'lua> {
/// Calls the table as function assuming it has `__call` metamethod.
///
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Asynchronously calls the table as function assuming it has `__call` metamethod.
@@ -771,7 +692,7 @@ pub trait TableExt<'lua>: Sealed {
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Gets the function associated to `key` from the table and executes it,
@@ -783,8 +704,8 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and executes it,
@@ -796,8 +717,8 @@ pub trait TableExt<'lua>: Sealed {
/// This might invoke the `__index` metamethod.
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
@@ -811,8 +732,8 @@ pub trait TableExt<'lua>: Sealed {
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Gets the function associated to `key` from the table and asynchronously executes it,
@@ -830,15 +751,15 @@ pub trait TableExt<'lua>: Sealed {
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
}
impl<'lua> TableExt<'lua> for Table<'lua> {
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
// Convert table to a function and call via pcall that respects the `__call` metamethod.
@@ -849,7 +770,7 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
Function(self.0.clone()).call_async(args)
@@ -857,20 +778,20 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let mut args = args.into_lua_multi(lua)?;
let mut args = args.to_lua_multi(lua)?;
args.push_front(Value::Table(self.clone()));
self.get::<_, Function>(key)?.call(args)
}
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
where
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.get::<_, Function>(key)?.call(args)
@@ -880,12 +801,12 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
let lua = self.0.lua;
let mut args = match args.into_lua_multi(lua) {
let mut args = match args.to_lua_multi(lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
@@ -897,8 +818,8 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
K: IntoLua<'lua>,
A: IntoLuaMulti<'lua>,
K: ToLua<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
match self.get::<_, Function>(key) {
@@ -973,16 +894,15 @@ where
fn next(&mut self) -> Option<Self::Item> {
if let Some(prev_key) = self.key.take() {
let lua = self.table.lua;
let state = lua.state();
let res = (|| unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
lua.push_ref(&self.table);
lua.push_value(prev_key)?;
let next = protect_lua!(state, 2, ffi::LUA_MULTRET, |state| {
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2)
})?;
if next != 0 {
@@ -1034,17 +954,16 @@ where
fn next(&mut self) -> Option<Self::Item> {
if let Some(index) = self.index.take() {
let lua = self.table.lua;
let state = lua.state();
let res = (|| unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 1 + if self.raw { 0 } else { 3 })?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?;
lua.push_ref(&self.table);
let res = if self.raw {
ffi::lua_rawgeti(state, -1, index)
ffi::lua_rawgeti(lua.state, -1, index)
} else {
protect_lua!(state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
};
match res {
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
@@ -1065,13 +984,3 @@ where
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Table: Send);
#[cfg(feature = "unstable")]
static_assertions::assert_not_impl_any!(OwnedTable: Send);
}
+47 -70
View File
@@ -5,7 +5,7 @@ use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
use crate::value::{FromLuaMulti, ToLuaMulti};
#[cfg(any(
feature = "lua54",
@@ -22,9 +22,9 @@ use {
},
futures_core::{future::Future, stream::Stream},
std::{
cell::RefCell,
marker::PhantomData,
pin::Pin,
ptr::NonNull,
task::{Context, Poll, Waker},
},
};
@@ -56,9 +56,10 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
/// [`Stream`]: futures_core::stream::Stream
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[derive(Debug)]
pub struct AsyncThread<'lua, R> {
thread: Thread<'lua>,
args0: Option<Result<MultiValue<'lua>>>,
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
ret: PhantomData<R>,
recycle: bool,
}
@@ -107,17 +108,15 @@ impl<'lua> Thread<'lua> {
/// ```
pub fn resume<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
let state = lua.state();
let mut args = args.into_lua_multi(lua)?;
let mut args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(state);
check_stack(state, cmp::max(nargs + 1, 3))?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
@@ -130,27 +129,23 @@ impl<'lua> Thread<'lua> {
for arg in args.drain_all() {
lua.push_value(arg)?;
}
ffi::lua_xmove(state, thread_state, nargs);
ffi::lua_xmove(lua.state, thread_state, nargs);
let mut nresults = 0;
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
if ret == ffi::LUA_ERRMEM {
// Don't call error handler for memory errors
return Err(pop_error(thread_state, ret));
}
check_stack(state, 3)?;
protect_lua!(state, 0, 1, |state| error_traceback_thread(
check_stack(lua.state, 3)?;
protect_lua!(lua.state, 0, 1, |state| error_traceback_thread(
state,
thread_state
))?;
return Err(pop_error(state, ret));
return Err(pop_error(lua.state, ret));
}
let mut results = args; // Reuse MultiValue container
check_stack(state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
ffi::lua_xmove(thread_state, state, nresults);
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
ffi::lua_xmove(thread_state, lua.state, nresults);
for _ in 0..nresults {
results.push_front(lua.pop_value());
@@ -199,32 +194,33 @@ impl<'lua> Thread<'lua> {
))]
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(state, -1);
let thread_state = ffi::lua_tothread(lua.state, -1);
#[cfg(feature = "lua54")]
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
let status = ffi::lua_resetthread(thread_state);
#[cfg(all(feature = "lua54", feature = "vendored"))]
let status = ffi::lua_closethread(thread_state, lua.state);
#[cfg(feature = "lua54")]
if status != ffi::LUA_OK {
return Err(pop_error(thread_state, status));
}
#[cfg(all(feature = "luajit", feature = "vendored"))]
ffi::lua_resetthread(state, thread_state);
ffi::lua_resetthread(lua.state, thread_state);
#[cfg(feature = "luau")]
ffi::lua_resetthread(thread_state);
lua.push_ref(&func.0);
ffi::lua_xmove(state, thread_state, 1);
ffi::lua_xmove(lua.state, thread_state, 1);
#[cfg(feature = "luau")]
{
// Inherit `LUA_GLOBALSINDEX` from the caller
ffi::lua_xpush(state, thread_state, ffi::LUA_GLOBALSINDEX);
ffi::lua_xpush(lua.state, thread_state, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
}
@@ -281,13 +277,13 @@ impl<'lua> Thread<'lua> {
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
where
A: IntoLuaMulti<'lua>,
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let args = args.into_lua_multi(self.0.lua);
let args = args.to_lua_multi(self.0.lua);
AsyncThread {
thread: self,
args0: Some(args),
args0: RefCell::new(Some(args)),
ret: PhantomData,
recycle: false,
}
@@ -329,15 +325,14 @@ impl<'lua> Thread<'lua> {
#[doc(hidden)]
pub fn sandbox(&self) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
check_stack(thread, 1)?;
check_stack(state, 3)?;
check_stack(lua.state, 3)?;
// Inherit `LUA_GLOBALSINDEX` from the caller
ffi::lua_xpush(state, thread, ffi::LUA_GLOBALSINDEX);
ffi::lua_xpush(lua.state, thread, ffi::LUA_GLOBALSINDEX);
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
}
}
}
@@ -395,14 +390,11 @@ where
_ => return Poll::Ready(None),
};
let _wg = WakerGuard::new(lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = unsafe { self.get_unchecked_mut() };
let ret: MultiValue = if let Some(args) = this.args0.take() {
this.thread.resume(args?)?
let _wg = WakerGuard::new(lua, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
this.thread.resume(())?
self.thread.resume(())?
};
if is_poll_pending(&ret) {
@@ -429,21 +421,18 @@ where
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
};
let _wg = WakerGuard::new(lua, cx.waker());
// This is safe as we are not moving the whole struct
let this = unsafe { self.get_unchecked_mut() };
let ret: MultiValue = if let Some(args) = this.args0.take() {
this.thread.resume(args?)?
let _wg = WakerGuard::new(lua, cx.waker().clone());
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
self.thread.resume(args?)?
} else {
this.thread.resume(())?
self.thread.resume(())?
};
if is_poll_pending(&ret) {
return Poll::Pending;
}
if let ThreadStatus::Resumable = this.thread.status() {
if let ThreadStatus::Resumable = self.thread.status() {
// Ignore value returned via yield()
cx.waker().wake_by_ref();
return Poll::Pending;
@@ -465,39 +454,27 @@ fn is_poll_pending(val: &MultiValue) -> bool {
}
#[cfg(feature = "async")]
struct WakerGuard<'lua, 'a> {
struct WakerGuard<'lua> {
lua: &'lua Lua,
prev: NonNull<Waker>,
_phantom: PhantomData<&'a ()>,
prev: Option<Waker>,
}
#[cfg(feature = "async")]
impl<'lua, 'a> WakerGuard<'lua, 'a> {
impl<'lua> WakerGuard<'lua> {
#[inline]
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
pub fn new(lua: &Lua, waker: Waker) -> Result<WakerGuard> {
unsafe {
let prev = lua.set_waker(NonNull::from(waker));
Ok(WakerGuard {
lua,
prev,
_phantom: PhantomData,
})
let prev = lua.set_waker(Some(waker));
Ok(WakerGuard { lua, prev })
}
}
}
#[cfg(feature = "async")]
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
impl<'lua> Drop for WakerGuard<'lua> {
fn drop(&mut self) {
unsafe {
self.lua.set_waker(self.prev);
self.lua.set_waker(self.prev.take());
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Thread: Send);
}
+7 -94
View File
@@ -63,10 +63,10 @@ pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
#[cfg(all(feature = "luau", feature = "send"))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState> + Send>;
#[cfg(all(feature = "luau", not(feature = "send")))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "lua54"))]
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
@@ -180,26 +180,6 @@ impl RegistryKey {
pub(crate) struct LuaRef<'lua> {
pub(crate) lua: &'lua Lua,
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,
index,
drop: true,
}
}
#[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
}
}
impl<'lua> fmt::Debug for LuaRef<'lua> {
@@ -216,8 +196,8 @@ impl<'lua> Clone for LuaRef<'lua> {
impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) {
if self.drop {
self.lua.drop_ref_index(self.index);
if self.index > 0 {
self.lua.drop_ref(self);
}
}
}
@@ -225,79 +205,12 @@ impl<'lua> Drop for LuaRef<'lua> {
impl<'lua> PartialEq for LuaRef<'lua> {
fn eq(&self, other: &Self) -> bool {
let lua = self.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(self);
lua.push_ref(other);
ffi::lua_rawequal(state, -1, -2) == 1
ffi::lua_rawequal(lua.state, -1, -2) == 1
}
}
}
#[cfg(feature = "unstable")]
pub(crate) struct LuaOwnedRef {
pub(crate) lua: Lua,
pub(crate) index: c_int,
_non_send: std::marker::PhantomData<*const ()>,
}
#[cfg(feature = "unstable")]
impl fmt::Debug for LuaOwnedRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OwnedRef({})", self.index)
}
}
#[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) {
self.lua.drop_ref_index(self.index);
}
}
#[cfg(feature = "unstable")]
impl LuaOwnedRef {
pub(crate) const fn new(lua: Lua, index: c_int) -> Self {
#[cfg(feature = "send")]
{
let _lua = lua;
let _index = index;
panic!("mlua must be compiled without \"send\" feature to use Owned types");
}
#[cfg(not(feature = "send"))]
LuaOwnedRef {
lua,
index,
_non_send: std::marker::PhantomData,
}
}
pub(crate) const fn to_ref(&self) -> LuaRef {
LuaRef {
lua: &self.lua,
index: self.index,
drop: false,
}
}
}
#[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);
}
+286 -327
View File
File diff suppressed because it is too large Load Diff
-205
View File
@@ -1,205 +0,0 @@
use crate::error::{Error, Result};
use crate::private::Sealed;
use crate::userdata::{AnyUserData, MetaMethod};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
#[cfg(feature = "async")]
use {futures_core::future::LocalBoxFuture, futures_util::future};
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
pub trait AnyUserDataExt<'lua>: 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>;
/// 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<()>;
/// Calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Asynchronously calls the userdata as a function assuming it has `__call` metamethod.
///
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Calls the userdata method, assuming it has `__index` metamethod
/// and a function associated to `name`.
fn call_method<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing the table itself along with `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_method<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
/// Gets the function associated to `key` from the table and executes it,
/// passing `args` as function arguments.
///
/// This is a shortcut for
/// `table.get::<_, Function>(key)?.call(args)`
///
/// This might invoke the `__index` metamethod.
fn call_function<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>;
/// Gets the function associated to `key` from the table and asynchronously executes it,
/// passing `args` as function arguments and returning Future.
///
/// Requires `feature = "async"`
///
/// This might invoke the `__index` metamethod.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn call_async_function<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut;
}
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&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)),
_ => Err(Error::RuntimeError(
"attempt to index a userdata value".to_string(),
)),
}
}
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&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)),
_ => Err(Error::RuntimeError(
"attempt to index a userdata value".to_string(),
)),
}
}
fn call<A, R>(&self, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let metatable = self.get_metatable()?;
match metatable.get::<Value>(MetaMethod::Call)? {
Value::Function(func) => func.call((self.clone(), args)),
_ => Err(Error::RuntimeError(
"attempt to call a userdata value".to_string(),
)),
}
}
#[cfg(feature = "async")]
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
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)) => func.call_async((self.clone(), args)),
Ok(_) => Box::pin(future::err(Error::RuntimeError(
"attempt to call a userdata value".to_string(),
))),
Err(err) => Box::pin(future::err(err)),
}
}
fn call_method<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
self.call_function(name, (self.clone(), args))
}
#[cfg(feature = "async")]
fn call_async_method<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
self.call_async_function(name, (self.clone(), args))
}
fn call_function<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
match self.get(name.as_ref())? {
Value::Function(func) => func.call(args),
val => Err(Error::RuntimeError(format!(
"attempt to call a {} value",
val.type_name()
))),
}
}
#[cfg(feature = "async")]
fn call_async_function<'fut, A, R>(
&self,
name: impl AsRef<str>,
args: A,
) -> LocalBoxFuture<'fut, Result<R>>
where
'lua: 'fut,
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua> + 'fut,
{
match self.get(name.as_ref()) {
Ok(Value::Function(func)) => func.call_async(args),
Ok(val) => Box::pin(future::err(Error::RuntimeError(format!(
"attempt to call a {} value",
val.type_name()
)))),
Err(err) => Box::pin(future::err(err)),
}
}
}
+360 -404
View File
@@ -1,7 +1,6 @@
use std::any::{self, TypeId};
use std::any::TypeId;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::string::String as StdString;
use std::sync::{Arc, Mutex, RwLock};
use crate::error::{Error, Result};
@@ -12,7 +11,7 @@ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{check_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
#[cfg(not(feature = "send"))]
use std::rc::Rc;
@@ -20,37 +19,23 @@ use std::rc::Rc;
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
futures_core::future::Future,
futures_util::future::{self, TryFutureExt},
std::future::Future,
};
pub struct UserDataRegistrar<'lua, T: 'static> {
// Fields
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
#[allow(clippy::type_complexity)]
pub(crate) meta_fields: Vec<(
String,
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
)>,
// Methods
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) struct StaticUserDataMethods<'lua, T: 'static + UserData> {
pub(crate) methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
pub(crate) async_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
pub(crate) meta_methods: Vec<(String, Callback<'lua, 'static>)>,
pub(crate) async_methods: Vec<(Vec<u8>, AsyncCallback<'lua, 'static>)>,
pub(crate) meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
pub(crate) async_meta_methods: Vec<(MetaMethod, AsyncCallback<'lua, 'static>)>,
_type: PhantomData<T>,
}
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
pub(crate) const fn new() -> Self {
UserDataRegistrar {
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
fn default() -> StaticUserDataMethods<'lua, T> {
StaticUserDataMethods {
methods: Vec::new(),
#[cfg(feature = "async")]
async_methods: Vec::new(),
@@ -60,372 +45,507 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
_type: PhantomData,
}
}
}
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
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))?
};
}
self.methods
.push((name.as_ref().to_vec(), Self::box_method(method)));
}
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_method_mut(method)));
}
#[cfg(feature = "async")]
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
where
T: Clone,
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
self.async_methods
.push((name.as_ref().to_vec(), Self::box_async_method(method)));
}
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_function(function)));
}
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.methods
.push((name.as_ref().to_vec(), Self::box_function_mut(function)));
}
#[cfg(feature = "async")]
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
where
S: AsRef<[u8]> + ?Sized,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
self.async_methods
.push((name.as_ref().to_vec(), Self::box_async_function(function)));
}
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_method(method)));
}
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, method: M)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_method_mut(method)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<S, A, R, M, MR>(&mut self, meta: S, method: M)
where
T: Clone,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
self.async_meta_methods
.push((meta.into(), Self::box_async_method(method)));
}
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_function(function)));
}
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
self.meta_methods
.push((meta.into(), Self::box_function_mut(function)));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_function<S, A, R, F, FR>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
self.async_meta_methods
.push((meta.into(), Self::box_async_function(function)));
}
// Below are internal methods used in generated code
fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_callback(&mut self, name: Vec<u8>, callback: AsyncCallback<'lua, 'static>) {
self.async_methods.push((name, callback));
}
fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) {
self.meta_methods.push((meta, callback));
}
#[cfg(feature = "async")]
fn add_async_meta_callback(
&mut self,
meta: MetaMethod,
callback: AsyncCallback<'lua, 'static>,
) {
self.async_meta_methods.push((meta, callback))
}
}
impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
fn box_method<A, R, M>(method: M) -> Callback<'lua, 'static>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
{
Box::new(move |lua, mut args| {
let front = args.pop_front();
let call = |ud| {
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args)?.into_lua_multi(lua)
};
if let Some(front) = front {
let state = lua.state();
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(get_userdata_ref::<T>(state));
call(&ud)
let ud = get_userdata_ref::<T>(lua.state)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_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));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
call(&ud)
let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
call(&ud)
let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
call(&ud)
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
call(&ud)
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
call(&ud)
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
Err(Error::FromLuaConversionError {
from: "missing argument",
to: "userdata",
message: None,
})
}
})
}
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
fn box_method_mut<A, R, M>(method: M) -> Callback<'lua, 'static>
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
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, mut args| {
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
let front = args.pop_front();
let call = |ud| {
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
method(lua, ud, args)?.into_lua_multi(lua)
};
if let Some(front) = front {
let state = lua.state();
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(get_userdata_mut::<T>(state));
call(&mut ud)
let mut ud = get_userdata_mut::<T>(lua.state)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[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));
let mut ud =
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
call(&mut ud)
let ud = get_userdata_mut::<Rc<RefCell<T>>>(lua.state)?;
let mut ud = ud
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state));
let ud = get_userdata_mut::<Arc<Mutex<T>>>(lua.state)?;
let mut ud =
try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
call(&mut ud)
ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state));
let ud = get_userdata_mut::<Arc<RwLock<T>>>(lua.state)?;
let mut ud =
try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
call(&mut ud)
ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let mut ud =
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
call(&mut ud)
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
Err(Error::FromLuaConversionError {
from: "missing argument",
to: "userdata",
message: None,
})
}
})
}
#[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
fn box_async_method<A, R, M, MR>(method: M) -> AsyncCallback<'lua, 'static>
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
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, mut args| {
let front = args.pop_front();
let call = |ud| {
// Self was at index 1, so we pass 2 here
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
Ok(method(lua, ud, args))
};
let fut_res = || {
if let Some(front) = front {
let state = lua.state();
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = get_userdata_ref::<T>(state)?;
call(ud.clone())
let ud = get_userdata_ref::<T>(lua.state)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, 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));
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
call(ud.clone())
let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
call(ud.clone())
let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
let ud = try_self_arg!(ud);
let ud =
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
call(ud.clone())
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
call(ud.clone())
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(feature = "parking_lot")]
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
let ud = try_self_arg!(ud);
let ud =
try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
call(ud.clone())
get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
Err(Error::bad_self_argument(&name, err))
Err(Error::FromLuaConversionError {
from: "missing argument",
to: "userdata",
message: None,
})
}
};
match fut_res() {
Ok(fut) => {
Box::pin(fut.and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
}
Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))),
Err(e) => Box::pin(future::err(e)),
}
})
}
fn box_function<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
fn box_function<A, R, F>(function: F) -> Callback<'lua, 'static>
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
{
let name = get_function_name::<T>(name);
Box::new(move |lua, args| {
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
})
Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua))
}
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
fn box_function_mut<A, R, F>(function: F) -> Callback<'lua, 'static>
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
{
let name = get_function_name::<T>(name);
let function = RefCell::new(function);
Box::new(move |lua, args| {
let function = &mut *function
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})
}
#[cfg(feature = "async")]
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'lua, 'static>
fn box_async_function<A, R, F, FR>(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>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
let name = get_function_name::<T>(name);
Box::new(move |lua, args| {
let args = match A::from_lua_multi_args(args, 1, Some(&name), lua) {
let args = match A::from_lua_multi(args, lua) {
Ok(args) => args,
Err(e) => return Box::pin(future::err(e)),
};
Box::pin(
function(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))),
)
Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
})
}
}
// Returns function name for the type `T`, without the module path
fn get_function_name<T: 'static>(name: &str) -> StdString {
let type_name = any::type_name::<T>().rsplit("::").next().unwrap();
format!("{type_name}.{name}",)
pub(crate) struct StaticUserDataFields<'lua, T: 'static + UserData> {
pub(crate) field_getters: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
pub(crate) field_setters: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
#[allow(clippy::type_complexity)]
pub(crate) meta_fields: Vec<(
MetaMethod,
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
)>,
_type: PhantomData<T>,
}
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
impl<'lua, T: 'static + UserData> Default for StaticUserDataFields<'lua, T> {
fn default() -> StaticUserDataFields<'lua, T> {
StaticUserDataFields {
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
_type: PhantomData,
}
}
}
impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFields<'lua, T> {
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
where
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
{
let name = name.as_ref();
let method = Self::box_method(name, move |lua, data, ()| method(lua, data));
self.field_getters.push((name.into(), method));
self.field_getters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::box_method(move |lua, data, ()| method(lua, data)),
));
}
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, method: M)
fn add_field_method_set<S, A, M>(&mut self, name: &S, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
{
let name = name.as_ref();
let method = Self::box_method_mut(name, method);
self.field_setters.push((name.into(), method));
self.field_setters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::box_method_mut(method),
));
}
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
where
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
S: AsRef<[u8]> + ?Sized,
R: ToLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
{
let name = name.as_ref();
let func = Self::box_function(name, function);
self.field_getters.push((name.into(), func));
self.field_getters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::<T>::box_function(function),
));
}
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
where
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
S: AsRef<[u8]> + ?Sized,
A: FromLua<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
{
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));
self.field_setters.push((
name.as_ref().to_vec(),
StaticUserDataMethods::<T>::box_function_mut(move |lua, (data, val)| {
function(lua, data, val)
}),
));
}
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
where
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>,
S: Into<MetaMethod>,
R: ToLua<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
{
let name = name.as_ref().to_string();
let meta = meta.into();
self.meta_fields.push((
name.clone(),
meta.clone(),
Box::new(move |lua| {
let value = f(lua)?.into_lua(lua)?;
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
let value = f(lua)?.to_lua(lua)?;
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
match value {
Value::Nil | Value::Table(_) | Value::Function(_) => {}
_ => {
return Err(Error::MetaMethodTypeError {
method: name.clone(),
method: meta.to_string(),
type_name: value.type_name(),
message: Some("expected nil, table or function".to_string()),
})
@@ -439,179 +559,15 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
// Below are internal methods
fn add_field_getter(&mut self, name: String, callback: Callback<'lua, 'static>) {
fn add_field_getter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
self.field_getters.push((name, callback));
}
fn add_field_setter(&mut self, name: String, callback: Callback<'lua, 'static>) {
fn add_field_setter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
self.field_setters.push((name, callback));
}
}
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'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<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_methods
.push((name.into(), Self::box_async_method(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<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
T: Clone,
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>,
{
let name = name.as_ref();
self.async_meta_methods
.push((name.into(), Self::box_async_method(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 add_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_callback(&mut self, name: String, callback: AsyncCallback<'lua, 'static>) {
self.async_methods.push((name, callback));
}
fn add_meta_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
self.meta_methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_meta_callback(&mut self, meta: String, callback: AsyncCallback<'lua, 'static>) {
self.async_meta_methods.push((meta, callback))
}
}
#[inline]
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result<Ref<'a, T>> {
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow()
@@ -624,9 +580,9 @@ unsafe fn get_userdata_mut<'a, T>(state: *mut ffi::lua_State) -> Result<RefMut<'
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
impl<T: 'static + UserData> UserData for $type {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
let mut orig_fields = UserDataRegistrar::new();
let mut orig_fields = StaticUserDataFields::default();
T::add_fields(&mut orig_fields);
for (name, callback) in orig_fields.field_getters {
fields.add_field_getter(name, callback);
@@ -637,7 +593,7 @@ macro_rules! lua_userdata_impl {
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
let mut orig_methods = UserDataRegistrar::new();
let mut orig_methods = StaticUserDataMethods::default();
T::add_methods(&mut orig_methods);
for (name, callback) in orig_methods.methods {
methods.add_callback(name, callback);
+31 -24
View File
@@ -12,7 +12,6 @@ use rustc_hash::FxHashMap;
use crate::error::{Error, Result};
use crate::ffi;
use crate::memory::MemoryState;
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
@@ -48,6 +47,7 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
pub struct StackGuard {
state: *mut ffi::lua_State,
top: c_int,
extra: c_int,
}
impl StackGuard {
@@ -59,6 +59,17 @@ impl StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
extra: 0,
}
}
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
#[inline]
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
extra,
}
}
}
@@ -67,11 +78,14 @@ impl Drop for StackGuard {
fn drop(&mut self) {
unsafe {
let top = ffi::lua_gettop(self.state);
if top < self.top {
if top < self.top + self.extra {
mlua_panic!("{} too many stack values popped", self.top - top)
}
if top > self.top {
ffi::lua_settop(self.state, self.top);
if top > self.top + self.extra {
if self.extra > 0 {
ffi::lua_rotate(self.state, self.top + 1, self.extra);
}
ffi::lua_settop(self.state, self.top + self.extra);
}
}
}
@@ -90,10 +104,8 @@ pub unsafe fn protect_lua_call(
) -> 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);
});
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
@@ -150,10 +162,8 @@ where
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>);
});
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);
}
@@ -263,7 +273,11 @@ 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 unsafe fn rawset_field<S>(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()>
where
S: AsRef<[u8]> + ?Sized,
{
let field = field.as_ref();
ffi::lua_pushvalue(state, table);
protect_lua!(state, 2, 0, |state| {
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
@@ -667,13 +681,6 @@ where
}
pub unsafe extern "C" 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.
@@ -862,7 +869,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// 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}");
let _ = write!(&mut (*err_buf), "{}", error);
Ok(err_buf)
}
Some(WrappedFailure::Panic(Some(ref panic))) => {
@@ -873,9 +880,9 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
ffi::lua_pop(state, 2);
if let Some(msg) = panic.downcast_ref::<&str>() {
let _ = write!(&mut (*err_buf), "{msg}");
let _ = write!(&mut (*err_buf), "{}", msg);
} else if let Some(msg) = panic.downcast_ref::<String>() {
let _ = write!(&mut (*err_buf), "{msg}");
let _ = write!(&mut (*err_buf), "{}", msg);
} else {
let _ = write!(&mut (*err_buf), "<panic>");
};
@@ -1024,7 +1031,7 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
let v = ffi::lua_tovector(state, index);
mlua_debug_assert!(!v.is_null(), "vector is null");
let (x, y, z) = (*v, *v.add(1), *v.add(2));
format!("vector({x},{y},{z})")
format!("vector({},{},{})", x, y, z)
}
ffi::LUA_TSTRING => {
let mut size = 0;
+12 -61
View File
@@ -1,7 +1,6 @@
use std::iter::{self, FromIterator};
use std::ops::Index;
use std::os::raw::c_void;
use std::sync::Arc;
use std::{ptr, slice, str, vec};
#[cfg(feature = "serialize")]
@@ -94,7 +93,7 @@ impl<'lua> Value<'lua> {
match (self, other.as_ref()) {
(Value::Table(a), Value::Table(b)) => a.equals(b),
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
(a, b) => Ok(a == b),
_ => Ok(self == other.as_ref()),
}
}
@@ -163,7 +162,8 @@ impl<'lua> Serialize for Value<'lua> {
Value::Boolean(b) => serializer.serialize_bool(*b),
#[allow(clippy::useless_conversion)]
Value::Integer(i) => serializer
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
.serialize_i64((*i).try_into().expect("cannot convert lua_Integer to i64")),
#[allow(clippy::useless_conversion)]
Value::Number(n) => serializer.serialize_f64(*n),
#[cfg(feature = "luau")]
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
@@ -180,34 +180,15 @@ impl<'lua> Serialize for Value<'lua> {
}
/// Trait for types convertible to `Value`.
pub trait IntoLua<'lua> {
pub trait ToLua<'lua> {
/// Performs the conversion.
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
}
/// Trait for types convertible from `Value`.
pub trait FromLua<'lua>: Sized {
/// Performs the conversion.
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
/// `i` is the argument index (position),
/// `to` is a function name that received the argument.
#[doc(hidden)]
fn from_lua_arg(
value: Value<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
Self::from_lua(value, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
fn from_lua(lua_value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
@@ -222,14 +203,8 @@ impl<'lua> MultiValue<'lua> {
/// Similar to `new` but can return previously used container with allocated capacity.
#[inline]
pub(crate) fn new_or_pooled(lua: &'lua Lua) -> MultiValue<'lua> {
lua.new_multivalue_from_pool()
}
/// Clears and returns previously allocated multivalue container to the pool.
#[inline]
pub(crate) fn return_to_pool(multivalue: Self, lua: &Lua) {
lua.return_multivalue_to_pool(multivalue);
pub(crate) fn new_or_cached(lua: &'lua Lua) -> MultiValue<'lua> {
lua.new_or_cached_multivalue()
}
}
@@ -362,11 +337,11 @@ impl<'lua> MultiValue<'lua> {
/// 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> {
/// This is a generalization of `ToLua`, allowing any number of resulting Lua values instead of just
/// one. Any type that implements `ToLua` will automatically implement this trait.
pub trait ToLuaMulti<'lua> {
/// Performs the conversion.
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
}
/// Trait for types that can be created from an arbitrary number of Lua values.
@@ -381,28 +356,4 @@ pub trait FromLuaMulti<'lua>: Sized {
/// 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>;
/// Performs the conversion for a list of arguments.
///
/// `i` is an index (position) of the first argument,
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_multi_args(
values: MultiValue<'lua>,
i: usize,
to: Option<&str>,
lua: &'lua Lua,
) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(values, lua)
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(Value: Send);
static_assertions::assert_not_impl_any!(MultiValue: Send);
}
+8
View File
@@ -0,0 +1,8 @@
[lua54_coverage]
features = "lua54,vendored,async,serialize,macros"
[lua51_coverage]
features = "lua51,vendored,async,serialize,macros"
[luau_coverage]
features = "luau,async,serialize,macros"
+130 -30
View File
@@ -1,15 +1,19 @@
#![cfg(feature = "async")]
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::{
atomic::{AtomicI64, AtomicU64, Ordering},
Arc,
};
use std::time::Duration;
use futures_timer::Delay;
use futures_util::stream::TryStreamExt;
use mlua::{
AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData,
UserDataMethods,
Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, Thread, UserData,
UserDataMethods, Value,
};
#[tokio::test]
@@ -26,20 +30,6 @@ async fn test_async_function() -> Result<()> {
Ok(())
}
#[cfg(feature = "unstable")]
#[tokio::test]
async fn test_async_function_wrap() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_async(|_, s: String| async move { Ok(s) });
lua.globals().set("f", f)?;
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
assert_eq!(res, "hello");
Ok(())
}
#[tokio::test]
async fn test_async_sleep() -> Result<()> {
let lua = Lua::new();
@@ -273,7 +263,7 @@ async fn test_async_thread() -> Result<()> {
#[tokio::test]
async fn test_async_table() -> Result<()> {
let options = LuaOptions::new().thread_pool_size(4);
let options = LuaOptions::new().thread_cache_size(4);
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let table = lua.create_table()?;
@@ -321,8 +311,8 @@ async fn test_async_table() -> Result<()> {
}
#[tokio::test]
async fn test_async_thread_pool() -> Result<()> {
let options = LuaOptions::new().thread_pool_size(4);
async fn test_async_thread_cache() -> Result<()> {
let options = LuaOptions::new().thread_cache_size(4);
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
let error_f = lua.create_async_function(|_, ()| async move {
@@ -433,14 +423,6 @@ async fn test_async_userdata() -> Result<()> {
.exec_async()
.await?;
userdata.call_async_method("set_value", 24).await?;
let n: u64 = userdata.call_async_method("get_value", ()).await?;
assert_eq!(n, 24);
userdata.call_async_function("sleep", 15).await?;
#[cfg(not(any(feature = "lua51", feature = "luau")))]
assert_eq!(userdata.call_async::<_, String>(()).await?, "elapsed:24ms");
Ok(())
}
@@ -457,7 +439,7 @@ async fn test_async_thread_error() -> Result<()> {
let lua = Lua::new();
let result = lua
.load("function x(...) error(...) end x(...)")
.set_name("chunk")
.set_name("chunk")?
.call_async::<_, ()>(MyUserData)
.await;
assert!(
@@ -467,3 +449,121 @@ async fn test_async_thread_error() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn test_async_scope() -> Result<()> {
let ref lua = Lua::new();
let ref rc = Rc::new(Cell::new(0));
let fut = lua.async_scope(|scope| async move {
let f = scope.create_async_function(move |_, n: u64| {
let rc2 = rc.clone();
async move {
rc2.set(42);
Delay::new(Duration::from_millis(n)).await;
assert_eq!(Rc::strong_count(&rc2), 2);
Ok(())
}
})?;
lua.globals().set("f", f.clone())?;
assert_eq!(Rc::strong_count(rc), 1);
let _ = f.call_async::<u64, ()>(10).await?;
assert_eq!(Rc::strong_count(rc), 1);
// Create future in partialy polled state (Poll::Pending)
let g = lua.create_thread(f)?;
g.resume::<u64, ()>(10)?;
lua.globals().set("g", g)?;
assert_eq!(Rc::strong_count(rc), 2);
Ok(())
});
assert_eq!(Rc::strong_count(rc), 1);
let _ = fut.await?;
assert_eq!(Rc::strong_count(rc), 1);
match lua
.globals()
.get::<_, Function>("f")?
.call_async::<_, ()>(10)
.await
{
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed function: {:?}", r),
};
Ok(())
}
#[tokio::test]
async fn test_async_scope_userdata() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_value", |_, data, ()| async move {
Delay::new(Duration::from_millis(10)).await;
Ok(data.0.load(Ordering::Relaxed))
});
methods.add_async_method("set_value", |_, data, n| async move {
Delay::new(Duration::from_millis(10)).await;
data.0.store(n, Ordering::Relaxed);
Ok(())
});
methods.add_async_function("sleep", |_, n| async move {
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
}
}
let ref lua = Lua::new();
let ref arc = Arc::new(AtomicI64::new(11));
lua.async_scope(|scope| async move {
let ud = scope.create_userdata(MyUserData(arc.clone()))?;
lua.globals().set("userdata", ud)?;
lua.load(
r#"
assert(userdata:get_value() == 11)
userdata:set_value(12)
assert(userdata.sleep(5) == "elapsed:5ms")
assert(userdata:get_value() == 12)
"#,
)
.exec_async()
.await
})
.await?;
assert_eq!(Arc::strong_count(arc), 1);
match lua.load("userdata:get_value()").exec_async().await {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
},
r => panic!("improper return for destructed userdata: {:?}", r),
};
Ok(())
}
+5 -4
View File
@@ -1,7 +1,7 @@
use std::fs;
use std::io;
use mlua::{Lua, Result};
use mlua::{Error, Lua, Result};
#[test]
fn test_chunk_path() -> Result<()> {
@@ -14,11 +14,12 @@ fn test_chunk_path() -> Result<()> {
return 321
"#,
)?;
let i: i32 = lua.load(&*temp_dir.path().join("module.lua")).eval()?;
let i: i32 = lua.load(&temp_dir.path().join("module.lua")).eval()?;
assert_eq!(i, 321);
match lua.load(&*temp_dir.path().join("module2.lua")).exec() {
Err(err) if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
match lua.load(&temp_dir.path().join("module2.lua")).exec() {
Err(Error::ExternalError(err))
if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
res => panic!("expected io::Error, got {:?}", res),
};
-33
View File
@@ -1,33 +0,0 @@
use mlua::{Error, ErrorContext, Lua, Result};
#[test]
fn test_error_context() -> Result<()> {
let lua = Lua::new();
let func = lua.create_function(|_, ()| {
Err::<(), _>(Error::RuntimeError("runtime error".into())).context("some context")
})?;
lua.globals().set("func", func)?;
let msg = lua
.load("local _, err = pcall(func); return tostring(err)")
.eval::<String>()?;
assert!(msg.contains("some context"));
assert!(msg.contains("runtime error"));
let func2 = lua.create_function(|lua, ()| {
lua.globals()
.get::<_, String>("nonextant")
.with_context(|_| "failed to find global")
})?;
lua.globals().set("func2", func2)?;
let msg2 = lua
.load("local _, err = pcall(func2); return tostring(err)")
.eval::<String>()?;
assert!(msg2.contains("failed to find global"));
println!("{msg2}");
assert!(msg2.contains("error converting Lua nil to String"));
Ok(())
}
+1 -34
View File
@@ -126,7 +126,7 @@ fn test_function_info() -> Result<()> {
end
"#,
)
.set_name("source1")
.set_name("source1")?
.exec()?;
let function1 = globals.get::<_, Function>("function1")?;
@@ -167,36 +167,3 @@ fn test_function_info() -> Result<()> {
Ok(())
}
#[cfg(feature = "unstable")]
#[test]
fn test_function_wrap() -> Result<()> {
use mlua::Error;
let lua = Lua::new();
lua.globals()
.set("f", Function::wrap(|_, s: String| Ok(s)))?;
lua.load(r#"assert(f("hello") == "hello")"#).exec().unwrap();
let mut _i = false;
lua.globals().set(
"f",
Function::wrap_mut(move |lua, ()| {
_i = true;
lua.globals().get::<_, Function>("f")?.call::<_, ()>(())
}),
)?;
match lua.globals().get::<_, Function>("f")?.call::<_, ()>(()) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::CallbackError { ref cause, .. } => match *cause.as_ref() {
Error::RecursiveMutCallback { .. } => {}
ref other => panic!("incorrect result: {other:?}"),
},
ref other => panic!("incorrect result: {other:?}"),
},
other => panic!("incorrect result: {other:?}"),
};
Ok(())
}
+3 -3
View File
@@ -173,7 +173,7 @@ fn test_interrupts() -> Result<()> {
let interrupts_count = Arc::new(AtomicU64::new(0));
let interrupts_count2 = interrupts_count.clone();
lua.set_interrupt(move |_| {
lua.set_interrupt(move || {
interrupts_count2.fetch_add(1, Ordering::Relaxed);
Ok(VmState::Continue)
});
@@ -195,7 +195,7 @@ fn test_interrupts() -> Result<()> {
//
let yield_count = Arc::new(AtomicU64::new(0));
let yield_count2 = yield_count.clone();
lua.set_interrupt(move |_| {
lua.set_interrupt(move || {
if yield_count2.fetch_add(1, Ordering::Relaxed) == 1 {
return Ok(VmState::Yield);
}
@@ -222,7 +222,7 @@ fn test_interrupts() -> Result<()> {
//
// Test errors in interrupts
//
lua.set_interrupt(|_| Err(Error::RuntimeError("error from interrupt".into())));
lua.set_interrupt(|| Err(Error::RuntimeError("error from interrupt".into())));
match f.call::<_, ()>(()) {
Err(Error::CallbackError { cause, .. }) => match *cause {
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
+5 -33
View File
@@ -1,7 +1,11 @@
use std::sync::Arc;
use mlua::{Error, GCMode, Lua, Result, UserData};
use mlua::{GCMode, Lua, Result, UserData};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use mlua::Error;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
#[test]
fn test_memory_limit() -> Result<()> {
let lua = Lua::new();
@@ -17,15 +21,6 @@ fn test_memory_limit() -> Result<()> {
.into_function()?;
f.call::<_, ()>(()).expect("should trigger no memory limit");
if cfg!(feature = "luajit") && cfg!(not(feature = "vendored")) {
// we don't support setting memory limit for non-vendored luajit
assert!(matches!(
lua.set_memory_limit(0),
Err(Error::MemoryLimitNotAvailable)
));
return Ok(());
}
lua.set_memory_limit(initial_memory + 10000)?;
match f.call::<_, ()>(()) {
Err(Error::MemoryError(_)) => {}
@@ -38,29 +33,6 @@ fn test_memory_limit() -> Result<()> {
Ok(())
}
#[test]
fn test_memory_limit_thread() -> Result<()> {
let lua = Lua::new();
let f = lua
.load("local t = {}; for i = 1,10000 do t[i] = i end")
.into_function()?;
if cfg!(feature = "luajit") && cfg!(not(feature = "vendored")) {
// we don't support setting memory limit for non-vendored luajit
return Ok(());
}
lua.set_memory_limit(lua.used_memory() + 10000)?;
let thread = lua.create_thread(f)?;
match thread.resume::<_, ()>(()) {
Err(Error::MemoryError(_)) => {}
something_else => panic!("did not trigger memory error: {:?}", something_else),
};
Ok(())
}
#[test]
fn test_gc_control() -> Result<()> {
let lua = Lua::new();
+2 -2
View File
@@ -1,8 +1,8 @@
[package]
name = "test_module"
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
edition = "2018"
[lib]
crate-type = ["cdylib"]
+1 -1
View File
@@ -2,7 +2,7 @@
name = "module_loader"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
edition = "2018"
[features]
lua54 = ["mlua/lua54"]
+5 -5
View File
@@ -8,7 +8,7 @@ fn test_module() -> Result<()> {
let lua = make_lua()?;
lua.load(
r#"
local mod = require("test_module")
local mod = require("rust_module")
assert(mod.sum(2,2) == 4)
"#,
)
@@ -20,8 +20,8 @@ fn test_module_multi() -> Result<()> {
let lua = make_lua()?;
lua.load(
r#"
local mod = require("test_module")
local mod2 = require("test_module.second")
local mod = require("rust_module")
local mod2 = require("rust_module.second")
assert(mod.check_userdata(mod2.userdata) == 123)
"#,
)
@@ -33,7 +33,7 @@ fn test_module_error() -> Result<()> {
let lua = make_lua()?;
lua.load(
r#"
local ok, err = pcall(require, "test_module.error")
local ok, err = pcall(require, "rust_module.error")
assert(not ok)
assert(string.find(tostring(err), "custom module error"))
"#,
@@ -55,7 +55,7 @@ fn test_module_from_thread() -> Result<()> {
local mod
local co = coroutine.create(function(a, b)
mod = require("test_module")
mod = require("rust_module")
assert(mod.sum(a, b) == a + b)
end)
+8 -8
View File
@@ -8,12 +8,12 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
fn check_userdata(_: &Lua, ud: LuaAnyUserData) -> LuaResult<i32> {
Ok(ud.borrow::<MyUserData>()?.0)
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
Ok(ud.0)
}
#[mlua::lua_module]
fn test_module(lua: &Lua) -> LuaResult<LuaTable> {
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
@@ -26,14 +26,14 @@ struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module(name = "test_module_second")]
fn test_module2(lua: &Lua) -> LuaResult<LuaTable> {
#[mlua::lua_module]
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", MyUserData(123))?;
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
Ok(exports)
}
#[mlua::lua_module]
fn test_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".into_lua_err())
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".to_lua_err())
}
-104
View File
@@ -356,107 +356,3 @@ fn test_scope_nonstatic_userdata_drop() -> Result<()> {
Ok(())
}
#[test]
fn test_scope_userdata_ref() -> Result<()> {
let lua = Lua::new();
struct MyUserData(Cell<i64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
methods.add_method("dec", |_, data, ()| {
data.0.set(data.0.get() - 1);
Ok(())
});
}
}
let data = MyUserData(Cell::new(1));
lua.scope(|scope| {
let ud = scope.create_userdata_ref(&data)?;
modify_userdata(&lua, ud)
})?;
assert_eq!(data.0.get(), 2);
Ok(())
}
#[test]
fn test_scope_userdata_ref_mut() -> Result<()> {
let lua = Lua::new();
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method_mut("inc", |_, data, ()| {
data.0 += 1;
Ok(())
});
methods.add_method_mut("dec", |_, data, ()| {
data.0 -= 1;
Ok(())
});
}
}
let mut data = MyUserData(1);
lua.scope(|scope| {
let ud = scope.create_userdata_ref_mut(&mut data)?;
modify_userdata(&lua, ud)
})?;
assert_eq!(data.0, 2);
Ok(())
}
#[test]
fn test_scope_any_userdata_ref() -> Result<()> {
let lua = Lua::new();
lua.register_userdata_type::<Cell<i64>>(|reg| {
reg.add_method("inc", |_, data, ()| {
data.set(data.get() + 1);
Ok(())
});
reg.add_method("dec", |_, data, ()| {
data.set(data.get() - 1);
Ok(())
});
})?;
let data = Cell::new(1i64);
lua.scope(|scope| {
let ud = scope.create_any_userdata_ref(&data)?;
modify_userdata(&lua, ud)
})?;
assert_eq!(data.get(), 2);
Ok(())
}
fn modify_userdata(lua: &Lua, ud: AnyUserData) -> Result<()> {
let f: Function = lua
.load(
r#"
function(u)
u:inc()
u:dec()
u:inc()
end
"#,
)
.eval()?;
f.call(ud)?;
Ok(())
}
+28 -75
View File
@@ -1,7 +1,6 @@
#![cfg(feature = "serialize")]
use std::collections::HashMap;
use std::error::Error as StdError;
use mlua::{
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
@@ -10,7 +9,7 @@ use mlua::{
use serde::{Deserialize, Serialize};
#[test]
fn test_serialize() -> Result<(), Box<dyn StdError>> {
fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Serialize)]
struct MyUserData(i64, String);
@@ -116,7 +115,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
}
#[test]
fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Serialize)]
struct MyUserData(i64);
@@ -147,7 +146,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
#[cfg(feature = "luau")]
#[test]
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
fn test_serialize_vector() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
let globals = lua.globals();
@@ -236,7 +235,7 @@ fn test_to_value_enum() -> LuaResult<()> {
}
#[test]
fn test_to_value_with_options() -> Result<(), Box<dyn StdError>> {
fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
let globals = lua.globals();
globals.set("null", lua.null())?;
@@ -306,7 +305,7 @@ fn test_to_value_with_options() -> Result<(), Box<dyn StdError>> {
}
#[test]
fn test_from_value_nested_tables() -> Result<(), Box<dyn StdError>> {
fn test_from_value_nested_tables() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
let value = lua
@@ -336,7 +335,7 @@ fn test_from_value_nested_tables() -> Result<(), Box<dyn StdError>> {
}
#[test]
fn test_from_value_struct() -> Result<(), Box<dyn StdError>> {
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
#[derive(Deserialize, PartialEq, Debug)]
@@ -377,7 +376,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn StdError>> {
}
#[test]
fn test_from_value_newtype_struct() -> Result<(), Box<dyn StdError>> {
fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
#[derive(Deserialize, PartialEq, Debug)]
@@ -390,38 +389,51 @@ fn test_from_value_newtype_struct() -> Result<(), Box<dyn StdError>> {
}
#[test]
fn test_from_value_enum() -> Result<(), Box<dyn StdError>> {
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
lua.globals().set("null", lua.null())?;
#[derive(Deserialize, PartialEq, Debug)]
enum E {
struct UnitStruct;
#[derive(Deserialize, PartialEq, Debug)]
enum E<T = ()> {
Unit,
Integer(u32),
Tuple(u32, u32),
Struct { a: u32 },
Wrap(T),
}
let value = lua.load(r#""Unit""#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Unit, got);
let value = lua.load(r#"{Integer = 1}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Integer(1), got);
let value = lua.load(r#"{Tuple = {1, 2}}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Tuple(1, 2), got);
let value = lua.load(r#"{Struct = {a = 3}}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Struct { a: 3 }, got);
let value = lua.load(r#"{Wrap = null}"#).eval()?;
let got = lua.from_value(value)?;
assert_eq!(E::Wrap(UnitStruct), got);
let value = lua.load(r#"{Wrap = null}"#).eval()?;
let got = lua.from_value(value)?;
assert_eq!(E::Wrap(()), got);
Ok(())
}
#[test]
fn test_from_value_enum_untagged() -> Result<(), Box<dyn StdError>> {
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
lua.globals().set("null", lua.null())?;
@@ -461,7 +473,7 @@ fn test_from_value_enum_untagged() -> Result<(), Box<dyn StdError>> {
}
#[test]
fn test_from_value_with_options() -> Result<(), Box<dyn StdError>> {
fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
// Deny unsupported types by default
@@ -516,62 +528,3 @@ fn test_from_value_with_options() -> Result<(), Box<dyn StdError>> {
Ok(())
}
#[test]
fn test_from_value_userdata() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new();
// Tuple struct
#[derive(Serialize, Deserialize)]
struct MyUserData(i64, String);
impl UserData for MyUserData {}
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
match lua.from_value::<MyUserData>(Value::UserData(ud)) {
Ok(_) => {}
Err(err) => panic!("expected no errors, got {err:?}"),
};
// Newtype struct
#[derive(Serialize, Deserialize)]
struct NewtypeUserdata(String);
impl UserData for NewtypeUserdata {}
let ud = lua.create_ser_userdata(NewtypeUserdata("newtype userdata".into()))?;
match lua.from_value::<NewtypeUserdata>(Value::UserData(ud)) {
Ok(_) => {}
Err(err) => panic!("expected no errors, got {err:?}"),
};
// Option
#[derive(Serialize, Deserialize)]
struct UnitUserdata;
impl UserData for UnitUserdata {}
let ud = lua.create_ser_userdata(UnitUserdata)?;
match lua.from_value::<Option<()>>(Value::UserData(ud)) {
Ok(Some(_)) => {}
Ok(_) => panic!("expected `Some`, got `None`"),
Err(err) => panic!("expected no errors, got {err:?}"),
};
// Destructed userdata with skip option
let ud = lua.create_ser_userdata(NewtypeUserdata("newtype userdata".into()))?;
let _ = ud.take::<NewtypeUserdata>()?;
match lua.from_value_with::<()>(
Value::UserData(ud),
DeserializeOptions::new().deny_unsupported_types(false),
) {
Ok(_) => {}
Err(err) => panic!("expected no errors, got {err:?}"),
};
Ok(())
}
-15
View File
@@ -83,18 +83,3 @@ fn test_string_hash() -> Result<()> {
Ok(())
}
#[test]
fn test_string_debug() -> Result<()> {
let lua = Lua::new();
// Valid utf8
let s = lua.create_string("hello")?;
assert_eq!(format!("{s:?}"), r#""hello""#);
// Invalid utf8
let s = lua.create_string(b"hello\0world\r\n\t\xF0\x90\x80")?;
assert_eq!(format!("{s:?}"), r#"b"hello\0world\r\n\t\xf0\x90\x80""#);
Ok(())
}
-47
View File
@@ -152,53 +152,6 @@ fn test_table_push_pop() -> Result<()> {
Ok(())
}
#[test]
fn test_table_clear() -> Result<()> {
let lua = Lua::new();
// Check readonly error
#[cfg(feature = "luau")]
{
let t = lua.create_table()?;
t.set_readonly(true);
assert!(matches!(
t.clear(),
Err(Error::RuntimeError(err)) if err.contains("attempt to modify a readonly table")
));
}
let t = lua.create_table()?;
// Set array and hash parts
t.push("abc")?;
t.push("bcd")?;
t.set("a", "1")?;
t.set("b", "2")?;
t.clear()?;
assert_eq!(t.len()?, 0);
assert_eq!(t.pairs::<Value, Value>().count(), 0);
// Test table with metamethods
let t2 = lua
.load(
r#"
setmetatable({1, 2, 3, a = "1"}, {
__index = function() error("index error") end,
__newindex = function() error("newindex error") end,
__len = function() error("len error") end,
__pairs = function() error("pairs error") end,
})
"#,
)
.eval::<Table>()?;
assert_eq!(t2.raw_len(), 3);
t2.clear()?;
assert_eq!(t2.raw_len(), 0);
assert_eq!(t2.raw_get::<_, Value>("a")?, Value::Nil);
assert_ne!(t2.get_metatable(), None);
Ok(())
}
#[test]
fn test_table_sequence_from() -> Result<()> {
let lua = Lua::new();
+18 -11
View File
@@ -69,7 +69,7 @@ fn test_safety() -> Result<()> {
fn test_load() -> Result<()> {
let lua = Lua::new();
let func = lua.load("return 1+2").into_function()?;
let func = lua.load("\treturn 1+2").into_function()?;
let result: i32 = func.call(())?;
assert_eq!(result, 3);
@@ -308,7 +308,7 @@ fn test_error() -> Result<()> {
.exec()?;
let rust_error_function =
lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?;
lua.create_function(|_, ()| -> Result<()> { Err(TestError.to_lua_err()) })?;
globals.set("rust_error_function", rust_error_function)?;
let no_error = globals.get::<_, Function>("no_error")?;
@@ -506,7 +506,7 @@ fn test_result_conversions() -> Result<()> {
let err = lua.create_function(|_, ()| {
Ok(Err::<String, _>(
"only through failure can we succeed".into_lua_err(),
"only through failure can we succeed".to_lua_err(),
))
})?;
let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
@@ -730,9 +730,9 @@ fn test_set_metatable_nil() -> Result<()> {
fn test_named_registry_value() -> Result<()> {
let lua = Lua::new();
lua.set_named_registry_value::<i32>("test", 42)?;
lua.set_named_registry_value::<_, i32>("test", 42)?;
let f = lua.create_function(move |lua, ()| {
assert_eq!(lua.named_registry_value::<i32>("test")?, 42);
assert_eq!(lua.named_registry_value::<_, i32>("test")?, 42);
Ok(())
})?;
@@ -991,7 +991,7 @@ fn test_ref_stack_exhaustion() {
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
let lua = Lua::new();
let mut vals = Vec::new();
for _ in 0..1000000 {
for _ in 0..10000000 {
vals.push(lua.create_table()?);
}
Ok(())
@@ -1072,7 +1072,7 @@ fn test_chunk_env() -> Result<()> {
test_var = 1
"#,
)
.set_environment(env1.clone())
.set_environment(env1.clone())?
.exec()?;
lua.load(
@@ -1081,11 +1081,18 @@ fn test_chunk_env() -> Result<()> {
test_var = 2
"#,
)
.set_environment(env2.clone())
.set_environment(env2.clone())?
.exec()?;
assert_eq!(lua.load("test_var").set_environment(env1).eval::<i32>()?, 1);
assert_eq!(lua.load("test_var").set_environment(env2).eval::<i32>()?, 2);
assert_eq!(
lua.load("test_var").set_environment(env1)?.eval::<i32>()?,
1
);
assert_eq!(
lua.load("test_var").set_environment(env2)?.eval::<i32>()?,
2
);
Ok(())
}
@@ -1220,7 +1227,7 @@ fn test_inspect_stack() -> Result<()> {
assert(logline("world") == '[string "chunk"]:12 world')
"#,
)
.set_name("chunk")
.set_name("chunk")?
.exec()?;
Ok(())
+31 -154
View File
@@ -1,4 +1,3 @@
use std::string::String as StdString;
use std::sync::Arc;
#[cfg(not(feature = "parking_lot"))]
use std::sync::{Mutex, RwLock};
@@ -13,12 +12,12 @@ use std::{cell::RefCell, rc::Rc};
use std::sync::atomic::{AtomicI64, Ordering};
use mlua::{
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result,
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value,
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String, UserData,
UserDataFields, UserDataMethods, Value,
};
#[test]
fn test_userdata() -> Result<()> {
fn test_user_data() -> Result<()> {
struct UserData1(i64);
struct UserData2(Box<i64>);
@@ -101,25 +100,20 @@ fn test_metamethods() -> Result<()> {
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): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 + rhs.0)),
);
methods.add_meta_function(
MetaMethod::Sub,
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
Ok(MyUserData(lhs.0 - rhs.0))
},
);
methods.add_meta_function(
MetaMethod::Eq,
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| Ok(lhs.0 == rhs.0),
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 - rhs.0)),
);
methods.add_meta_function(MetaMethod::Eq, |_, (lhs, rhs): (MyUserData, MyUserData)| {
Ok(lhs.0 == rhs.0)
});
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
if index.to_str()? == "inner" {
Ok(data.0)
} else {
Err("no such custom index".into_lua_err())
Err("no such custom index".to_lua_err())
}
});
#[cfg(any(
@@ -130,14 +124,13 @@ fn test_metamethods() -> Result<()> {
))]
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): (MyUserData, 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))
});
}
@@ -149,9 +142,7 @@ fn test_metamethods() -> Result<()> {
globals.set("userdata2", MyUserData(3))?;
globals.set("userdata3", MyUserData(3))?;
assert_eq!(
lua.load("userdata1 + userdata2")
.eval::<UserDataRef<MyUserData>>()?
.0,
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
10
);
@@ -175,12 +166,7 @@ fn test_metamethods() -> Result<()> {
)
.eval::<Function>()?;
assert_eq!(
lua.load("userdata1 - userdata2")
.eval::<UserDataRef<MyUserData>>()?
.0,
4
);
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
@@ -319,19 +305,21 @@ fn test_userdata_take() -> Result<()> {
fn check_userdata_take(lua: &Lua, userdata: AnyUserData, rc: Arc<i64>) -> Result<()> {
lua.globals().set("userdata", userdata.clone())?;
assert_eq!(Arc::strong_count(&rc), 3);
let userdata_copy = userdata.clone();
{
let _value = userdata.borrow::<MyUserdata>()?;
// We should not be able to take userdata if it's borrowed
match userdata.take::<MyUserdata>() {
match userdata_copy.take::<MyUserdata>() {
Err(Error::UserDataBorrowMutError) => {}
r => panic!("expected `UserDataBorrowMutError` error, got {:?}", r),
}
}
let value = userdata.take::<MyUserdata>()?;
let value = userdata_copy.take::<MyUserdata>()?;
assert_eq!(*value.0, 18);
drop(value);
assert_eq!(Arc::strong_count(&rc), 2);
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&rc), 1);
match userdata.borrow::<MyUserdata>() {
Err(Error::UserDataDestructed) => {}
@@ -344,13 +332,6 @@ fn test_userdata_take() -> Result<()> {
},
r => panic!("improper return for destructed userdata: {:?}", r),
}
drop(userdata);
lua.globals().raw_remove("userdata")?;
lua.gc_collect()?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&rc), 1);
Ok(())
}
@@ -422,9 +403,9 @@ fn test_user_values() -> Result<()> {
ud.set_named_user_value("name", "alex")?;
ud.set_named_user_value("age", 10)?;
assert_eq!(ud.get_named_user_value::<String>("name")?, "alex");
assert_eq!(ud.get_named_user_value::<i32>("age")?, 10);
assert_eq!(ud.get_named_user_value::<Value>("nonexist")?, Value::Nil);
assert_eq!(ud.get_named_user_value::<_, String>("name")?, "alex");
assert_eq!(ud.get_named_user_value::<_, i32>("age")?, 10);
assert_eq!(ud.get_named_user_value::<_, Value>("nonexist")?, Value::Nil);
Ok(())
}
@@ -548,7 +529,7 @@ fn test_metatable() -> Result<()> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("my_type_name", |_, data: AnyUserData| {
let metatable = data.get_metatable()?;
metatable.get::<String>("__type_name")
metatable.get::<_, String>("__type_name")
});
}
}
@@ -566,7 +547,7 @@ fn test_metatable() -> Result<()> {
let ud: AnyUserData = globals.get("ud")?;
let metatable = ud.get_metatable()?;
match metatable.get::<Value>("__gc") {
match metatable.get::<_, Value>("__gc") {
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
Err(Error::MetaMethodRestricted(_)) => {}
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
@@ -580,10 +561,11 @@ fn test_metatable() -> Result<()> {
let mut methods = metatable
.pairs()
.into_iter()
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
.collect::<Result<Vec<_>>>()?;
methods.sort();
assert_eq!(methods, vec!["__index", "__type_name"]);
methods.sort_by_cached_key(|k| k.name().to_owned());
assert_eq!(methods, vec![MetaMethod::Index, "__type_name".into()]);
#[derive(Copy, Clone)]
struct MyUserData2(i64);
@@ -715,108 +697,3 @@ fn test_userdata_proxy() -> Result<()> {
)
.exec()
}
#[test]
fn test_any_userdata() -> Result<()> {
let lua = Lua::new();
lua.register_userdata_type::<StdString>(|reg| {
reg.add_method("get", |_, this, ()| Ok(this.clone()));
reg.add_method_mut("concat", |_, this, s: String| {
this.push_str(&s.to_string_lossy());
Ok(())
});
})?;
let ud = lua.create_any_userdata("hello".to_string())?;
assert_eq!(&*ud.borrow::<StdString>()?, "hello");
lua.globals().set("ud", ud)?;
lua.load(
r#"
assert(ud:get() == "hello")
ud:concat(", world")
assert(ud:get() == "hello, world")
"#,
)
.exec()
.unwrap();
Ok(())
}
#[test]
fn test_userdata_ext() -> Result<()> {
let lua = Lua::new();
#[derive(Clone, Copy)]
struct MyUserData(u32);
impl UserData for MyUserData {
fn add_fields<'lua, F: UserDataFields<'lua, 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;
Ok(())
});
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Call, |_, _this, ()| Ok("called"));
methods.add_method_mut("add", |_, this, x: u32| {
this.0 += x;
Ok(())
});
}
}
let ud = lua.create_userdata(MyUserData(123))?;
assert_eq!(ud.get::<_, u32>("n")?, 123);
ud.set("n", 321)?;
assert_eq!(ud.get::<_, u32>("n")?, 321);
match ud.get::<_, u32>("non-existent") {
Err(Error::RuntimeError(_)) => {}
r => panic!("expected RuntimeError, got {r:?}"),
}
match ud.set::<_, u32>("non-existent", 123) {
Err(Error::RuntimeError(_)) => {}
r => panic!("expected RuntimeError, got {r:?}"),
}
assert_eq!(ud.call::<_, String>(())?, "called");
ud.call_method("add", 2)?;
assert_eq!(ud.get::<_, u32>("n")?, 323);
Ok(())
}
#[test]
fn test_userdata_method_errors() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("get_value", |_, data, ()| Ok(data.0));
}
}
let lua = Lua::new();
let ud = lua.create_userdata(MyUserData(123))?;
let res = ud.call_function::<_, ()>("get_value", ());
let Err(Error::CallbackError { cause, .. }) = res else {
panic!("expected CallbackError, got {res:?}");
};
assert!(matches!(
&*cause,
Error::BadArgument {
to,
name,
..
} if to.as_deref() == Some("MyUserData.get_value") && name.as_deref() == Some("self")
));
Ok(())
}