mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 628b70f561 | |||
| 8a7e546c66 | |||
| 97bd288f56 | |||
| 59253410bb | |||
| 5fb7b96704 | |||
| 17162b1e0b | |||
| 934aa3b65f | |||
| 3bffe1d7c3 | |||
| 582b2c585f | |||
| 56b6b128b5 | |||
| 1731f5d61b | |||
| 60822d12d2 | |||
| 8ff610529b | |||
| adbc9ccc9b | |||
| 84fe5f7f76 | |||
| 1fe583027b | |||
| 7b5b78fa3d | |||
| 41aae83304 | |||
| fc84e8623e | |||
| 8aae9a7c3e | |||
| 24bbd62564 | |||
| 30af919931 | |||
| 89b5981c59 |
+40
-11
@@ -24,17 +24,18 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }},vendored"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --release --features "${{ matrix.lua }}"
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
@@ -47,7 +48,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
@@ -64,7 +65,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
@@ -87,7 +88,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
@@ -122,16 +123,43 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }},vendored"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
name: Test with address sanitizer
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
@@ -141,7 +169,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest]
|
||||
rust: [stable, nightly]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
@@ -155,6 +183,7 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
|
||||
|
||||
+13
-2
@@ -1,3 +1,14 @@
|
||||
## v0.6.2
|
||||
|
||||
- New functionality: `Lua::load_from_function()` and `Lua::create_c_function()`
|
||||
- Many optimizations in callbacks/userdata creation and methods execution
|
||||
|
||||
## v0.6.1
|
||||
|
||||
- Update `chunk!` documentation (stable Rust limitations)
|
||||
- Fixed Lua sequence table conversion to HashSet/BTreeSet
|
||||
- `once_cell` dependency lowered to 1.0
|
||||
|
||||
## v0.6.0
|
||||
Changes since 0.5.4
|
||||
- New `UserDataFields` API
|
||||
@@ -7,7 +18,7 @@ Changes since 0.5.4
|
||||
- Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
|
||||
- Added `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`.
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads (coroutines) with attaching a new function.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
|
||||
- Improved errors reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in a module mode.
|
||||
- Added `String::to_string_lossy`
|
||||
- Various bugfixes and improvements
|
||||
@@ -16,7 +27,7 @@ Breaking changes:
|
||||
- Errors are always `Send + Sync` to be compatible with the anyhow crate.
|
||||
- Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
|
||||
- Removed `Result` from `Function::dump()` (never fails)
|
||||
- Removed `AnyUserData::has_metamethod()` (if favour of full access to metatables)
|
||||
- Removed `AnyUserData::has_metamethod()` (in favour of full access to metatables)
|
||||
|
||||
## v0.6.0-beta.3
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.6.0" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.2" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -43,7 +43,7 @@ macros = ["mlua_derive/macros"]
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.7" }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
|
||||
@@ -59,6 +59,22 @@ fn create_string_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [function] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for i in 0..10 {
|
||||
lua.create_function(move |_, ()| Ok(i)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -258,6 +274,7 @@ criterion_group! {
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
only_pulls: true
|
||||
project:
|
||||
default:
|
||||
only_pulls: true
|
||||
+12
-12
@@ -554,17 +554,17 @@ impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T
|
||||
|
||||
impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua> for HashSet<T, S> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
.collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "HashSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,17 +579,17 @@ impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
|
||||
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
.collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "BTreeSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -80,6 +80,7 @@ pub const LUA_ERRERR: c_int = 5;
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
pub const LUA_ERRERR: c_int = 6;
|
||||
|
||||
/// A raw Lua state associated with a thread.
|
||||
pub type lua_State = c_void;
|
||||
|
||||
// basic types
|
||||
@@ -121,14 +122,14 @@ pub type lua_Number = luaconf::LUA_NUMBER;
|
||||
/// A Lua integer, usually equivalent to `i64`.
|
||||
pub type lua_Integer = luaconf::LUA_INTEGER;
|
||||
|
||||
// unsigned integer type
|
||||
/// A Lua unsigned integer, usually equivalent to `u64`.
|
||||
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
|
||||
|
||||
// type for continuation-function contexts
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
pub type lua_KContext = luaconf::LUA_KCONTEXT;
|
||||
|
||||
/// Type for native functions that can be passed to Lua.
|
||||
/// Type for native C functions that can be passed to Lua.
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Type for continuation functions
|
||||
|
||||
+13
-7
@@ -72,7 +72,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.0")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.2")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -98,8 +98,7 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::ffi::lua_State;
|
||||
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
|
||||
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
@@ -139,9 +138,9 @@ extern crate mlua_derive;
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
///
|
||||
/// Captured variables are moved into the chunk.
|
||||
/// Captured variables are **moved** into the chunk.
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, chunk};
|
||||
@@ -165,12 +164,19 @@ extern crate mlua_derive;
|
||||
/// (Single quoted strings only work if they contain a single character, since in Rust,
|
||||
/// `'a'` is a character literal).
|
||||
///
|
||||
/// - Using Lua comments `--` is not desirable in **stable** Rust and can have bad side effects.
|
||||
///
|
||||
/// This is because procedural macros have Line/Column information available only in
|
||||
/// **nightly** Rust. Instead, Lua chunks represented as a big single line of code in stable Rust.
|
||||
///
|
||||
/// As workaround, Rust comments `//` can be used.
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals.
|
||||
/// - Certain escape codes in string literals don't work.
|
||||
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
///
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
|
||||
+267
-215
@@ -6,7 +6,7 @@ use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -19,17 +19,18 @@ use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{
|
||||
Callback, HookCallback, Integer, LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
|
||||
Callback, CallbackUpvalue, HookCallback, Integer, LightUserData, LuaRef, MaybeSend, Number,
|
||||
RegistryKey,
|
||||
};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{
|
||||
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
|
||||
get_gc_metatable_for, get_gc_userdata, get_main_state, get_userdata, get_wrapped_error,
|
||||
init_error_registry, init_gc_metatable_for, init_userdata_metatable, pop_error, protect_lua,
|
||||
push_gc_userdata, push_string, push_table, push_userdata, push_wrapped_error, rawset_field,
|
||||
safe_pcall, safe_xpcall, StackGuard, WrappedError, WrappedPanic,
|
||||
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
|
||||
init_gc_metatable, init_userdata_metatable, pop_error, protect_lua, push_gc_userdata,
|
||||
push_string, push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall, StackGuard,
|
||||
WrappedFailure,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
@@ -38,7 +39,7 @@ use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_core::{
|
||||
future::{Future, LocalBoxFuture},
|
||||
task::{Context, Poll, Waker},
|
||||
@@ -54,7 +55,7 @@ use serde::Serialize;
|
||||
pub struct Lua {
|
||||
pub(crate) state: *mut ffi::lua_State,
|
||||
main_state: Option<*mut ffi::lua_State>,
|
||||
extra: Arc<Mutex<ExtraData>>,
|
||||
extra: *mut ExtraData,
|
||||
ephemeral: bool,
|
||||
safe: bool,
|
||||
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
||||
@@ -76,9 +77,9 @@ struct ExtraData {
|
||||
ref_stack_top: c_int,
|
||||
ref_free: Vec<c_int>,
|
||||
|
||||
// Vec of preallocated WrappedError/WrappedPanic structs
|
||||
// Vec of preallocated WrappedFailure enums
|
||||
// Used for callback optimization
|
||||
prealloc_wrapped_errors: Vec<c_int>,
|
||||
prealloc_wrapped_failures: Vec<c_int>,
|
||||
|
||||
hook_callback: Option<HookCallback>,
|
||||
}
|
||||
@@ -162,8 +163,8 @@ impl Drop for Lua {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.ephemeral {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
for index in extra.prealloc_wrapped_errors.clone() {
|
||||
let extra = &mut *self.extra;
|
||||
for index in extra.prealloc_wrapped_failures.clone() {
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, index);
|
||||
extra.ref_free.push(index);
|
||||
@@ -173,13 +174,12 @@ impl Drop for Lua {
|
||||
&& extra.ref_stack_top as usize == extra.ref_free.len(),
|
||||
"reference leak detected"
|
||||
);
|
||||
let mut unref_list =
|
||||
mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned");
|
||||
*unref_list = None;
|
||||
*mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned") = None;
|
||||
ffi::lua_close(mlua_expect!(self.main_state, "main_state is null"));
|
||||
if !extra.mem_info.is_null() {
|
||||
Box::from_raw(extra.mem_info);
|
||||
}
|
||||
Box::from_raw(extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@ impl Lua {
|
||||
mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
lua.safe = true;
|
||||
mlua_expect!(lua.extra.lock(), "extra is poisoned").safe = true;
|
||||
unsafe { (*lua.extra).safe = true };
|
||||
|
||||
Ok(lua)
|
||||
}
|
||||
@@ -341,16 +341,19 @@ impl Lua {
|
||||
|
||||
let mut lua = Lua::init_from_ptr(state);
|
||||
lua.ephemeral = false;
|
||||
|
||||
let extra = &mut *lua.extra;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
mlua_expect!(lua.extra.lock(), "extra is poisoned").mem_info = mem_info;
|
||||
extra.mem_info = mem_info;
|
||||
}
|
||||
|
||||
mlua_expect!(
|
||||
load_from_std_lib(state, libs),
|
||||
"Error during loading standard libraries"
|
||||
);
|
||||
mlua_expect!(lua.extra.lock(), "extra is poisoned").libs |= libs;
|
||||
extra.libs |= libs;
|
||||
|
||||
if !options.catch_rust_panics {
|
||||
mlua_expect!(
|
||||
@@ -395,19 +398,19 @@ impl Lua {
|
||||
// Create the internal metatables and place them in the registry
|
||||
// to prevent them from being garbage collected.
|
||||
|
||||
init_gc_metatable_for::<Callback>(state, None)?;
|
||||
init_gc_metatable_for::<Lua>(state, None)?;
|
||||
init_gc_metatable_for::<Weak<Mutex<ExtraData>>>(state, None)?;
|
||||
init_gc_metatable::<Callback>(state, None)?;
|
||||
init_gc_metatable::<CallbackUpvalue>(state, None)?;
|
||||
#[cfg(feature = "async")]
|
||||
{
|
||||
init_gc_metatable_for::<AsyncCallback>(state, None)?;
|
||||
init_gc_metatable_for::<LocalBoxFuture<Result<MultiValue>>>(state, None)?;
|
||||
init_gc_metatable_for::<Option<Waker>>(state, None)?;
|
||||
init_gc_metatable::<AsyncCallback>(state, None)?;
|
||||
init_gc_metatable::<AsyncCallbackUpvalue>(state, None)?;
|
||||
init_gc_metatable::<AsyncPollUpvalue>(state, None)?;
|
||||
init_gc_metatable::<Option<Waker>>(state, None)?;
|
||||
|
||||
// Create empty Waker slot
|
||||
push_gc_userdata::<Option<Waker>>(state, None)?;
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
protect_lua(state, 1, 0, |state| {
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
})?;
|
||||
}
|
||||
@@ -431,7 +434,7 @@ impl Lua {
|
||||
|
||||
// Create ExtraData
|
||||
|
||||
let extra = Arc::new(Mutex::new(ExtraData {
|
||||
let extra = Box::into_raw(Box::new(ExtraData {
|
||||
registered_userdata: HashMap::new(),
|
||||
registered_userdata_mt: HashSet::new(),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
@@ -443,17 +446,14 @@ impl Lua {
|
||||
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
||||
ref_stack_top: 0,
|
||||
ref_free: Vec::new(),
|
||||
prealloc_wrapped_errors: Vec::new(),
|
||||
prealloc_wrapped_failures: Vec::new(),
|
||||
hook_callback: None,
|
||||
}));
|
||||
|
||||
mlua_expect!(
|
||||
push_gc_userdata(main_state, Arc::downgrade(&extra)),
|
||||
"Error while storing extra data",
|
||||
);
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_pushlightuserdata(main_state, extra as *mut c_void);
|
||||
mlua_expect!(
|
||||
protect_lua(main_state, 1, 0, |state| {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key)
|
||||
}),
|
||||
"Error while storing extra data",
|
||||
@@ -499,15 +499,64 @@ impl Lua {
|
||||
let res = unsafe { load_from_std_lib(state, libs) };
|
||||
|
||||
// If `package` library loaded into a safe lua state then disable C modules
|
||||
let curr_libs = mlua_expect!(self.extra.lock(), "extra is poisoned").libs;
|
||||
let extra = unsafe { &mut *self.extra };
|
||||
let curr_libs = extra.libs;
|
||||
if self.safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(self.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
mlua_expect!(self.extra.lock(), "extra is poisoned").libs |= libs;
|
||||
extra.libs |= libs;
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Loads module `modname` into an existing Lua state using the specified entrypoint
|
||||
/// function.
|
||||
///
|
||||
/// Internally calls the Lua function `func` with the string `modname` as an argument,
|
||||
/// sets the call result to `package.loaded[modname]` and returns copy of the result.
|
||||
///
|
||||
/// If `package.loaded[modname]` value is not nil, returns copy of the value without
|
||||
/// calling the function.
|
||||
///
|
||||
/// If the function does not return a non-nil value then this method assigns true to
|
||||
/// `package.loaded[modname]`.
|
||||
///
|
||||
/// Behavior is similar to Lua's [`require`] function.
|
||||
///
|
||||
/// [`require`]: https://www.lua.org/manual/5.3/manual.html#pdf-require
|
||||
pub fn load_from_function<'lua, S, T>(
|
||||
&'lua self,
|
||||
modname: &S,
|
||||
func: Function<'lua>,
|
||||
) -> Result<T>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
T: FromLua<'lua>,
|
||||
{
|
||||
let loaded = unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
protect_lua(self.state, 0, 1, |state| {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
})?;
|
||||
Table(self.pop_ref())
|
||||
};
|
||||
|
||||
let modname = self.create_string(modname)?;
|
||||
let value = match loaded.raw_get(modname.clone())? {
|
||||
Value::Nil => {
|
||||
let result = match func.call(modname.clone())? {
|
||||
Value::Nil => Value::Boolean(true),
|
||||
res => res,
|
||||
};
|
||||
loaded.raw_set(modname, result.clone())?;
|
||||
result
|
||||
}
|
||||
res => res,
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
|
||||
/// Consumes and leaks `Lua` object, returning a static reference `&'static Lua`.
|
||||
///
|
||||
/// This function is useful when the `Lua` object is supposed to live for the remainder
|
||||
@@ -599,8 +648,7 @@ impl Lua {
|
||||
{
|
||||
let state = self.main_state.ok_or(Error::MainThreadNotAvailable)?;
|
||||
unsafe {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.hook_callback = Some(Arc::new(RefCell::new(callback)));
|
||||
(*self.extra).hook_callback = Some(Arc::new(RefCell::new(callback)));
|
||||
ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
|
||||
}
|
||||
Ok(())
|
||||
@@ -614,26 +662,26 @@ impl Lua {
|
||||
Some(state) => state,
|
||||
None => return,
|
||||
};
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
unsafe {
|
||||
extra.hook_callback = None;
|
||||
(*self.extra).hook_callback = None;
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the amount of memory (in bytes) currently used inside this Lua state.
|
||||
pub fn used_memory(&self) -> usize {
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let state = self.main_state.unwrap_or(self.state);
|
||||
if extra.mem_info.is_null() {
|
||||
// Get data from the Lua GC
|
||||
unsafe {
|
||||
let used_kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0);
|
||||
return (used_kbytes as usize) * 1024 + (used_kbytes_rem as usize);
|
||||
unsafe {
|
||||
let state = self.main_state.unwrap_or(self.state);
|
||||
match (*self.extra).mem_info {
|
||||
mem_info if mem_info.is_null() => {
|
||||
// Get data from the Lua GC
|
||||
let used_kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0);
|
||||
(used_kbytes as usize) * 1024 + (used_kbytes_rem as usize)
|
||||
}
|
||||
mem_info => (*mem_info).used_memory as usize,
|
||||
}
|
||||
}
|
||||
unsafe { (*extra.mem_info).used_memory as usize }
|
||||
}
|
||||
|
||||
/// Sets a memory limit (in bytes) on this Lua state.
|
||||
@@ -647,14 +695,15 @@ impl Lua {
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
pub fn set_memory_limit(&self, memory_limit: usize) -> Result<usize> {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
if extra.mem_info.is_null() {
|
||||
return Err(Error::MemoryLimitNotAvailable);
|
||||
}
|
||||
unsafe {
|
||||
let prev_limit = (*extra.mem_info).memory_limit as usize;
|
||||
(*extra.mem_info).memory_limit = memory_limit as isize;
|
||||
Ok(prev_limit)
|
||||
match (*self.extra).mem_info {
|
||||
mem_info if mem_info.is_null() => Err(Error::MemoryLimitNotAvailable),
|
||||
mem_info => {
|
||||
let prev_limit = (*mem_info).memory_limit as usize;
|
||||
(*mem_info).memory_limit = memory_limit as isize;
|
||||
Ok(prev_limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1032,6 +1081,16 @@ impl Lua {
|
||||
})
|
||||
}
|
||||
|
||||
/// Wraps a C function, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe because provides a way to execute unsafe C function.
|
||||
pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> {
|
||||
check_stack(self.state, 1)?;
|
||||
ffi::lua_pushcfunction(self.state, func);
|
||||
Ok(Function(self.pop_ref()))
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// While executing the function Rust will poll Future and if the result is not ready, call
|
||||
@@ -1375,11 +1434,9 @@ impl Lua {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
|
||||
Ok(RegistryKey {
|
||||
registry_id,
|
||||
unref_list: extra.registry_unref_list.clone(),
|
||||
unref_list: (*self.extra).registry_unref_list.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1435,8 +1492,8 @@ impl Lua {
|
||||
/// `Error::MismatchedRegistryKey` if passed a `RegistryKey` that was not created with a
|
||||
/// matching `Lua` state.
|
||||
pub fn owns_registry_value(&self, key: &RegistryKey) -> bool {
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
Arc::ptr_eq(&key.unref_list, &extra.registry_unref_list)
|
||||
let registry_unref_list = unsafe { &(*self.extra).registry_unref_list };
|
||||
Arc::ptr_eq(&key.unref_list, registry_unref_list)
|
||||
}
|
||||
|
||||
/// Remove any registry values whose `RegistryKey`s have all been dropped.
|
||||
@@ -1446,9 +1503,10 @@ impl Lua {
|
||||
/// by `Lua::remove_registry_value`.
|
||||
pub fn expire_registry_values(&self) {
|
||||
unsafe {
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let mut unref_list =
|
||||
mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned");
|
||||
let mut unref_list = mlua_expect!(
|
||||
(*self.extra).registry_unref_list.lock(),
|
||||
"unref list poisoned"
|
||||
);
|
||||
let unref_list = mem::replace(&mut *unref_list, Some(Vec::new()));
|
||||
for id in mlua_expect!(unref_list, "unref list not set") {
|
||||
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||
@@ -1499,8 +1557,8 @@ impl Lua {
|
||||
self.push_ref(&ud.0);
|
||||
}
|
||||
|
||||
Value::Error(e) => {
|
||||
push_wrapped_error(self.state, e)?;
|
||||
Value::Error(err) => {
|
||||
push_gc_userdata(self.state, WrappedFailure::Error(err))?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1549,20 +1607,22 @@ impl Lua {
|
||||
ffi::LUA_TUSERDATA => {
|
||||
// We must prevent interaction with userdata types other than UserData OR a WrappedError.
|
||||
// WrappedPanics are automatically resumed.
|
||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
||||
let err = err.clone();
|
||||
ffi::lua_pop(state, 1);
|
||||
Value::Error(err)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
||||
if let Some(panic) = (*panic).0.take() {
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
let err = err.clone();
|
||||
ffi::lua_pop(state, 1);
|
||||
resume_unwind(panic);
|
||||
Value::Error(err)
|
||||
}
|
||||
// Previously resumed panic?
|
||||
ffi::lua_pop(state, 1);
|
||||
Nil
|
||||
} else {
|
||||
Value::UserData(AnyUserData(self.pop_ref()))
|
||||
Some(WrappedFailure::Panic(panic)) => {
|
||||
if let Some(panic) = panic.take() {
|
||||
ffi::lua_pop(state, 1);
|
||||
resume_unwind(panic);
|
||||
}
|
||||
// Previously resumed panic?
|
||||
ffi::lua_pop(state, 1);
|
||||
Nil
|
||||
}
|
||||
_ => Value::UserData(AnyUserData(self.pop_ref())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1575,12 +1635,11 @@ impl Lua {
|
||||
// Pushes a LuaRef value onto the stack, uses 1 stack space, does not call checkstack
|
||||
pub(crate) unsafe fn push_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) {
|
||||
assert!(
|
||||
Arc::ptr_eq(&lref.lua.extra, &self.extra),
|
||||
lref.lua.extra == self.extra,
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
ffi::lua_xmove(extra.ref_thread, self.state, 1);
|
||||
ffi::lua_pushvalue((*self.extra).ref_thread, lref.index);
|
||||
ffi::lua_xmove((*self.extra).ref_thread, self.state, 1);
|
||||
}
|
||||
|
||||
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
||||
@@ -1593,24 +1652,24 @@ impl Lua {
|
||||
// number of short term references being created, and `RegistryKey` being used for long term
|
||||
// references.
|
||||
pub(crate) unsafe fn pop_ref(&self) -> LuaRef {
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let extra = &mut *self.extra;
|
||||
ffi::lua_xmove(self.state, extra.ref_thread, 1);
|
||||
let (index, _) = ref_stack_pop(extra);
|
||||
let index = ref_stack_pop(extra);
|
||||
LuaRef { lua: self, index }
|
||||
}
|
||||
|
||||
pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> {
|
||||
unsafe {
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let extra = &mut *self.extra;
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
let (index, _) = ref_stack_pop(extra);
|
||||
let index = ref_stack_pop(extra);
|
||||
LuaRef { lua: self, index }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn drop_ref<'lua>(&'lua self, lref: &mut LuaRef<'lua>) {
|
||||
unsafe {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let extra = &mut *self.extra;
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, lref.index);
|
||||
extra.ref_free.push(lref.index);
|
||||
@@ -1619,10 +1678,7 @@ impl Lua {
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> {
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
|
||||
.registered_userdata
|
||||
.get(&type_id)
|
||||
{
|
||||
if let Some(&table_id) = (*self.extra).registered_userdata.get(&type_id) {
|
||||
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, table_id as Integer);
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1717,21 +1773,19 @@ impl Lua {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let extra = &mut *self.extra;
|
||||
extra.registered_userdata.insert(type_id, id);
|
||||
extra.registered_userdata_mt.insert(ptr as isize);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn register_userdata_metatable(&self, id: isize) {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.registered_userdata_mt.insert(id);
|
||||
pub(crate) unsafe fn register_userdata_metatable(&self, id: isize) {
|
||||
(*self.extra).registered_userdata_mt.insert(id);
|
||||
}
|
||||
|
||||
pub(crate) fn deregister_userdata_metatable(&self, id: isize) {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.registered_userdata_mt.remove(&id);
|
||||
pub(crate) unsafe fn deregister_userdata_metatable(&self, id: isize) {
|
||||
(*self.extra).registered_userdata_mt.remove(&id);
|
||||
}
|
||||
|
||||
// Pushes a LuaRef value onto the stack, checking that it's a registered
|
||||
@@ -1744,7 +1798,7 @@ impl Lua {
|
||||
}
|
||||
// Check that userdata is registered
|
||||
let ptr = ffi::lua_topointer(self.state, -1);
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let extra = &*self.extra;
|
||||
if extra.registered_userdata_mt.contains(&(ptr as isize)) {
|
||||
if !with_mt {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
@@ -1777,22 +1831,22 @@ impl Lua {
|
||||
'lua: 'callback,
|
||||
{
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error2(state, |nargs| {
|
||||
let upvalue_idx1 = ffi::lua_upvalueindex(1);
|
||||
let upvalue_idx2 = ffi::lua_upvalueindex(2);
|
||||
if ffi::lua_type(state, upvalue_idx1) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL
|
||||
{
|
||||
let get_extra = |state| {
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).lua.extra
|
||||
};
|
||||
callback_error_ext(state, get_extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let func = get_userdata::<Callback>(state, upvalue_idx1);
|
||||
let lua = get_userdata::<Lua>(state, upvalue_idx2);
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, upvalue_idx);
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
let lua = &mut (*upvalue).lua;
|
||||
lua.state = state;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
@@ -1801,7 +1855,7 @@ impl Lua {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
let results = (*func)(lua, args)?;
|
||||
let results = ((*upvalue).func)(lua, args)?;
|
||||
let nresults = results.len() as c_int;
|
||||
|
||||
check_stack(state, nresults)?;
|
||||
@@ -1815,12 +1869,13 @@ impl Lua {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 5)?;
|
||||
check_stack(self.state, 4)?;
|
||||
|
||||
push_gc_userdata::<Callback>(self.state, mem::transmute(func))?;
|
||||
push_gc_userdata(self.state, self.clone())?;
|
||||
protect_lua(self.state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, call_callback, 2);
|
||||
let lua = self.clone();
|
||||
let func = mem::transmute(func);
|
||||
push_gc_userdata(self.state, CallbackUpvalue { lua, func })?;
|
||||
protect_lua(self.state, 1, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
})?;
|
||||
|
||||
Ok(Function(self.pop_ref()))
|
||||
@@ -1837,29 +1892,29 @@ impl Lua {
|
||||
{
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
let libs = mlua_expect!(self.extra.lock(), "extra is poisoned").libs;
|
||||
let libs = unsafe { (*self.extra).libs };
|
||||
if !libs.contains(StdLib::COROUTINE) {
|
||||
self.load_from_std_lib(StdLib::COROUTINE)?;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error2(state, |nargs| {
|
||||
let upvalue_idx1 = ffi::lua_upvalueindex(1);
|
||||
let upvalue_idx2 = ffi::lua_upvalueindex(2);
|
||||
if ffi::lua_type(state, upvalue_idx1) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL
|
||||
{
|
||||
let get_extra = |state| {
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).lua.extra
|
||||
};
|
||||
callback_error_ext(state, get_extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let func = get_userdata::<AsyncCallback>(state, upvalue_idx1);
|
||||
let lua = get_userdata::<Lua>(state, upvalue_idx2);
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, upvalue_idx);
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
let lua = &mut (*upvalue).lua;
|
||||
lua.state = state;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
@@ -1868,12 +1923,11 @@ impl Lua {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
let fut = (*func)(lua, args);
|
||||
push_gc_userdata(state, fut)?;
|
||||
push_gc_userdata(state, lua.clone())?;
|
||||
|
||||
protect_lua(state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, poll_future, 2);
|
||||
let fut = ((*upvalue).func)(lua, args);
|
||||
let lua = lua.clone();
|
||||
push_gc_userdata(state, AsyncPollUpvalue { lua, fut })?;
|
||||
protect_lua(state, 1, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, poll_future, 1);
|
||||
})?;
|
||||
|
||||
Ok(1)
|
||||
@@ -1881,22 +1935,22 @@ impl Lua {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error2(state, |nargs| {
|
||||
let upvalue_idx1 = ffi::lua_upvalueindex(1);
|
||||
let upvalue_idx2 = ffi::lua_upvalueindex(2);
|
||||
if ffi::lua_type(state, upvalue_idx1) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL
|
||||
{
|
||||
let get_extra = |state| {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).lua.extra
|
||||
};
|
||||
callback_error_ext(state, get_extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let fut = get_userdata::<LocalBoxFuture<Result<MultiValue>>>(state, upvalue_idx1);
|
||||
let lua = get_userdata::<Lua>(state, upvalue_idx2);
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, upvalue_idx);
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
let lua = &mut (*upvalue).lua;
|
||||
lua.state = state;
|
||||
|
||||
// Try to get an outer poll waker
|
||||
@@ -1910,7 +1964,8 @@ impl Lua {
|
||||
|
||||
let mut ctx = Context::from_waker(&waker);
|
||||
|
||||
match (*fut).as_mut().poll(&mut ctx) {
|
||||
let fut = &mut (*upvalue).fut;
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => {
|
||||
check_stack(state, 1)?;
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
@@ -1932,12 +1987,13 @@ impl Lua {
|
||||
|
||||
let get_poll = unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 5)?;
|
||||
check_stack(self.state, 4)?;
|
||||
|
||||
push_gc_userdata::<AsyncCallback>(self.state, mem::transmute(func))?;
|
||||
push_gc_userdata(self.state, self.clone())?;
|
||||
protect_lua(self.state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, call_callback, 2);
|
||||
let lua = self.clone();
|
||||
let func = mem::transmute(func);
|
||||
push_gc_userdata(self.state, AsyncCallbackUpvalue { lua, func })?;
|
||||
protect_lua(self.state, 1, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
})?;
|
||||
|
||||
Function(self.pop_ref())
|
||||
@@ -1987,11 +2043,10 @@ impl Lua {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 2)?;
|
||||
|
||||
// If we unable to push metatable, then we should not push userdata.
|
||||
// Otherwise we can have a memory leak.
|
||||
self.push_userdata_metatable::<T>()?;
|
||||
// It's safe to push userdata first and then metatable.
|
||||
// If the first push failed, unlikely we moved `data` to allocated memory.
|
||||
push_userdata(self.state, data)?;
|
||||
ffi::lua_rotate(self.state, -2, 1);
|
||||
self.push_userdata_metatable::<T>()?;
|
||||
ffi::lua_setmetatable(self.state, -2);
|
||||
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
@@ -2001,7 +2056,7 @@ impl Lua {
|
||||
Lua {
|
||||
state: self.state,
|
||||
main_state: self.main_state,
|
||||
extra: self.extra.clone(),
|
||||
extra: self.extra,
|
||||
ephemeral: true,
|
||||
safe: self.safe,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
@@ -2039,30 +2094,24 @@ impl Lua {
|
||||
assert_stack(state, 1);
|
||||
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TLIGHTUSERDATA {
|
||||
return None;
|
||||
}
|
||||
let extra = mlua_expect!(
|
||||
(*get_gc_userdata::<Weak<Mutex<ExtraData>>>(state, -1)).upgrade(),
|
||||
"extra is destroyed"
|
||||
);
|
||||
let extra = ffi::lua_touserdata(state, -1) as *mut ExtraData;
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
let safe = mlua_expect!(extra.lock(), "extra is poisoned").safe;
|
||||
|
||||
Some(Lua {
|
||||
state,
|
||||
main_state: get_main_state(state),
|
||||
extra,
|
||||
ephemeral: true,
|
||||
safe,
|
||||
safe: (*extra).safe,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn hook_callback(&self) -> Option<HookCallback> {
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.hook_callback.clone()
|
||||
(*self.extra).hook_callback.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2285,15 +2334,16 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
|
||||
}
|
||||
}
|
||||
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedError+Panic` userdata
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||
// and instead reuses unsed and cached values from previous calls (or allocates new).
|
||||
// It assumes that ephemeral `Lua` struct is passed as a 2nd upvalue.
|
||||
pub unsafe fn callback_error2<F, R>(state: *mut ffi::lua_State, f: F) -> R
|
||||
// It requires `get_extra` function to return `ExtraData` value.
|
||||
unsafe fn callback_error_ext<E, F, R>(state: *mut ffi::lua_State, get_extra: E, f: F) -> R
|
||||
where
|
||||
E: Fn(*mut ffi::lua_State) -> *mut ExtraData,
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
{
|
||||
let upvalue_idx2 = ffi::lua_upvalueindex(2);
|
||||
if ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
return callback_error(state, f);
|
||||
}
|
||||
|
||||
@@ -2307,64 +2357,60 @@ where
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
|
||||
enum PreallocatedError {
|
||||
New(*mut c_void),
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Cached(i32),
|
||||
}
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
let lua = get_userdata::<Lua>(state, upvalue_idx2);
|
||||
let prealloc_err = {
|
||||
let mut extra = mlua_expect!((*lua).extra.lock(), "extra is poisoned");
|
||||
match extra.prealloc_wrapped_errors.pop() {
|
||||
Some(index) => PreallocatedError::Cached(index),
|
||||
let extra = &mut *get_extra(state);
|
||||
let prealloc_failure = {
|
||||
match extra.prealloc_wrapped_failures.pop() {
|
||||
Some(index) => PreallocatedFailure::Cached(index),
|
||||
None => {
|
||||
let size = mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>());
|
||||
let ud = ffi::lua_newuserdata(state, size);
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
PreallocatedError::New(ud)
|
||||
PreallocatedFailure::New(ud as *mut WrappedFailure)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let get_prealloc_err = || {
|
||||
let mut extra = mlua_expect!((*lua).extra.lock(), "extra is poisoned");
|
||||
match prealloc_err {
|
||||
PreallocatedError::New(ud) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedError::Cached(index) => {
|
||||
ffi::lua_settop(state, 0);
|
||||
ffi::lua_pushvalue(extra.ref_thread, index);
|
||||
ffi::lua_xmove(extra.ref_thread, state, 1);
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, index);
|
||||
extra.ref_free.push(index);
|
||||
ffi::lua_touserdata(state, -1)
|
||||
}
|
||||
let mut get_prealloc_failure = || match prealloc_failure {
|
||||
PreallocatedFailure::New(ud) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedFailure::Cached(index) => {
|
||||
ffi::lua_settop(state, 0);
|
||||
ffi::lua_pushvalue(extra.ref_thread, index);
|
||||
ffi::lua_xmove(extra.ref_thread, state, 1);
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, index);
|
||||
extra.ref_free.push(index);
|
||||
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
|
||||
}
|
||||
};
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused WrappedError+Panic to the cache
|
||||
let mut extra = mlua_expect!((*lua).extra.lock(), "extra is poisoned");
|
||||
match prealloc_err {
|
||||
PreallocatedError::New(_) if extra.prealloc_wrapped_errors.len() < 16 => {
|
||||
// Return unused WrappedFailure to the cache
|
||||
match prealloc_failure {
|
||||
PreallocatedFailure::New(_) if extra.prealloc_wrapped_failures.len() < 16 => {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, extra.ref_thread, 1);
|
||||
let (index, mut extra) = ref_stack_pop(extra);
|
||||
extra.prealloc_wrapped_errors.push(index);
|
||||
let index = ref_stack_pop(extra);
|
||||
extra.prealloc_wrapped_failures.push(index);
|
||||
}
|
||||
PreallocatedError::New(_) => {
|
||||
PreallocatedFailure::New(_) => {
|
||||
ffi::lua_remove(state, 1);
|
||||
}
|
||||
PreallocatedError::Cached(index) if extra.prealloc_wrapped_errors.len() < 16 => {
|
||||
extra.prealloc_wrapped_errors.push(index);
|
||||
PreallocatedFailure::Cached(index)
|
||||
if extra.prealloc_wrapped_failures.len() < 16 =>
|
||||
{
|
||||
extra.prealloc_wrapped_failures.push(index);
|
||||
}
|
||||
PreallocatedError::Cached(index) => {
|
||||
PreallocatedFailure::Cached(index) => {
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, index);
|
||||
extra.ref_free.push(index);
|
||||
@@ -2373,9 +2419,9 @@ where
|
||||
r
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
let wrapped_error = get_prealloc_err() as *mut WrappedError;
|
||||
ptr::write(wrapped_error, WrappedError(err));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
let wrapped_error = get_prealloc_failure();
|
||||
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
// Convert to CallbackError and attach traceback
|
||||
@@ -2387,15 +2433,17 @@ where
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
let cause = Arc::new((*wrapped_error).0.clone());
|
||||
(*wrapped_error).0 = Error::CallbackError { traceback, cause };
|
||||
if let WrappedFailure::Error(ref mut err) = *wrapped_error {
|
||||
let cause = Arc::new(err.clone());
|
||||
*err = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
let wrapped_panic = get_prealloc_err() as *mut WrappedPanic;
|
||||
ptr::write(wrapped_panic, WrappedPanic(Some(p)));
|
||||
get_gc_metatable_for::<WrappedPanic>(state);
|
||||
let wrapped_panic = get_prealloc_failure();
|
||||
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
@@ -2524,10 +2572,10 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
|
||||
}
|
||||
|
||||
// We move `extra` (`MutexGuard`) here to correctly drop it if panic
|
||||
unsafe fn ref_stack_pop(mut extra: MutexGuard<ExtraData>) -> (c_int, MutexGuard<ExtraData>) {
|
||||
unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int {
|
||||
if let Some(free) = extra.ref_free.pop() {
|
||||
ffi::lua_replace(extra.ref_thread, free);
|
||||
return (free, extra);
|
||||
return free;
|
||||
}
|
||||
|
||||
// Try to grow max stack size
|
||||
@@ -2541,7 +2589,6 @@ unsafe fn ref_stack_pop(mut extra: MutexGuard<ExtraData>) -> (c_int, MutexGuard<
|
||||
// during unwinding.
|
||||
ffi::lua_pop(extra.ref_thread, 1);
|
||||
let top = extra.ref_stack_top;
|
||||
drop(extra);
|
||||
// It is a user error to create enough references to exhaust the Lua max stack size for
|
||||
// the ref thread.
|
||||
panic!(
|
||||
@@ -2552,7 +2599,7 @@ unsafe fn ref_stack_pop(mut extra: MutexGuard<ExtraData>) -> (c_int, MutexGuard<
|
||||
extra.ref_stack_size += inc;
|
||||
}
|
||||
extra.ref_stack_top += 1;
|
||||
(extra.ref_stack_top, extra)
|
||||
extra.ref_stack_top
|
||||
}
|
||||
|
||||
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
||||
@@ -2767,11 +2814,16 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
match userdata.type_id()? {
|
||||
id if id == TypeId::of::<T>() => {
|
||||
let mut ud = userdata.borrow_mut::<T>()?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
// Try normal userdata first
|
||||
let err = match userdata.borrow_mut::<T>() {
|
||||
Ok(mut ud) => {
|
||||
return method(lua, &mut ud, A::from_lua_multi(args, lua)?)?
|
||||
.to_lua_multi(lua)
|
||||
}
|
||||
Err(err) => err,
|
||||
};
|
||||
match userdata.type_id()? {
|
||||
id if id == TypeId::of::<T>() => Err(err),
|
||||
#[cfg(not(feature = "send"))]
|
||||
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
|
||||
|
||||
+10
-26
@@ -13,7 +13,7 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
@@ -25,8 +25,8 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_core::future::Future,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
};
|
||||
|
||||
@@ -224,7 +224,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// use [`Scope::create_userdata`] instead.
|
||||
///
|
||||
/// The main limitation that comes from using non-'static userdata is that the produced userdata
|
||||
/// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for
|
||||
/// will no longer have a `TypeId` associated with it, because `TypeId` can only work for
|
||||
/// 'static types. This means that it is impossible, once the userdata is created, to get a
|
||||
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
|
||||
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
|
||||
@@ -460,16 +460,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<Callback>(state);
|
||||
let ud = take_userdata::<CallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
vec![Box::new(ud)]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
@@ -510,32 +505,21 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(ud1), Box::new(ud2)];
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
|
||||
|
||||
// Finally, get polled future and destroy it
|
||||
f.lua.push_ref(&poll_str.0);
|
||||
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud3 = take_userdata::<LocalBoxFuture<Result<MultiValue>>>(state);
|
||||
let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
data.push(Box::new(ud3));
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud4 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
data.push(Box::new(ud4));
|
||||
data.push(Box::new(upvalue2));
|
||||
}
|
||||
|
||||
data
|
||||
|
||||
@@ -26,10 +26,27 @@ pub struct LightUserData(pub *mut c_void);
|
||||
pub(crate) type Callback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
||||
|
||||
pub(crate) struct CallbackUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) func: Callback<'lua, 'static>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct AsyncCallbackUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) func: AsyncCallback<'lua, 'static>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct AsyncPollUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
|
||||
}
|
||||
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
+33
-109
@@ -11,6 +11,7 @@ use std::future::Future;
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::os::raw::c_void,
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
@@ -607,116 +608,31 @@ impl<T> UserDataCell<T> {
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value.
|
||||
fn try_borrow(&self) -> Result<UserDataRef<T>> {
|
||||
fn try_borrow(&self) -> Result<Ref<T>> {
|
||||
self.0
|
||||
.try_borrow()
|
||||
.map(|r| UserDataRef(UserDataRefInner::Ref(r)))
|
||||
.map(|r| Ref::map(r, |r| r.deref()))
|
||||
.map_err(|_| Error::UserDataBorrowError)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value.
|
||||
fn try_borrow_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
fn try_borrow_mut(&self) -> Result<RefMut<T>> {
|
||||
self.0
|
||||
.try_borrow_mut()
|
||||
.map(|r| UserDataRefMut(UserDataRefMutInner::Ref(r)))
|
||||
.map(|r| RefMut::map(r, |r| r.deref_mut()))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataCell<()> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let ser = self
|
||||
.0
|
||||
.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
|
||||
.ser;
|
||||
unsafe { (&*ser).serialize(serializer) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRef<'a, T>(UserDataRefInner<'a, T>);
|
||||
|
||||
enum UserDataRefInner<'a, T> {
|
||||
Ref(Ref<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRefMut<'a, T>(UserDataRefMutInner<'a, T>);
|
||||
|
||||
enum UserDataRefMutInner<'a, T> {
|
||||
Ref(RefMut<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0 {
|
||||
UserDataRefInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match &mut self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &mut *x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
pub(crate) enum UserDataWrapped<T> {
|
||||
Default(T),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
Serializable(*mut T, *const dyn erased_serde::Serialize),
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
fn new(data: T) -> Self {
|
||||
UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}
|
||||
UserDataWrapped::Default(data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -725,21 +641,15 @@ impl<T> UserDataWrapped<T> {
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}
|
||||
UserDataWrapped::Serializable(data_raw, data_raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drop(Box::from_raw(self.data));
|
||||
#[cfg(feature = "serialize")]
|
||||
if self.data as *mut () != self.ser as *mut () {
|
||||
drop(Box::from_raw(self.ser));
|
||||
}
|
||||
if let UserDataWrapped::Serializable(data, _) = *self {
|
||||
drop(unsafe { Box::from_raw(data) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,13 +658,21 @@ impl<T> Deref for UserDataWrapped<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.data }
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &**data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.data }
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &mut **data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,7 +724,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
///
|
||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<UserDataRef<T>> {
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
|
||||
self.inspect(|cell| cell.try_borrow())
|
||||
}
|
||||
|
||||
@@ -816,7 +734,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
///
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<UserDataRefMut<T>> {
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
@@ -1069,8 +987,14 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
|
||||
lua.push_userdata_ref(&self.0, false)
|
||||
.map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
||||
ud.serialize(serializer)
|
||||
let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
|
||||
let data =
|
||||
ud.0.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
match *data {
|
||||
UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
|
||||
UserDataWrapped::Serializable(_, ser) => (&*ser).serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+116
-129
@@ -167,41 +167,45 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
"pop_error called with non-error return code"
|
||||
);
|
||||
|
||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
||||
if let Some(p) = (*panic).0.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
Error::PreviouslyResumedPanic
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
}
|
||||
} else {
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
Some(WrappedFailure::Panic(panic)) => {
|
||||
if let Some(p) = panic.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
Error::PreviouslyResumedPanic
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
match err_code {
|
||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||
ffi::LUA_ERRSYNTAX => {
|
||||
Error::SyntaxError {
|
||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||
// stock Lua REPL does.
|
||||
incomplete_input: err_string.ends_with("<eof>")
|
||||
|| err_string.ends_with("'<eof>'"),
|
||||
message: err_string,
|
||||
match err_code {
|
||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||
ffi::LUA_ERRSYNTAX => {
|
||||
Error::SyntaxError {
|
||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||
// stock Lua REPL does.
|
||||
incomplete_input: err_string.ends_with("<eof>")
|
||||
|| err_string.ends_with("'<eof>'"),
|
||||
message: err_string,
|
||||
}
|
||||
}
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||
_ => mlua_panic!("unrecognized lua error code"),
|
||||
}
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||
_ => mlua_panic!("unrecognized lua error code"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +236,7 @@ where
|
||||
protect_lua(state, 2, 0, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
ffi::lua_rotate(state, -3, 2);
|
||||
ffi::lua_rawset(state, -3)
|
||||
ffi::lua_rawset(state, -3);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -271,7 +275,7 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
push_userdata(state, t)?;
|
||||
get_gc_metatable_for::<T>(state);
|
||||
get_gc_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
@@ -282,7 +286,7 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
get_gc_metatable_for::<T>(state);
|
||||
get_gc_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
@@ -409,7 +413,7 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
}
|
||||
}
|
||||
protect_lua(state, 3, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, meta_index_impl, 3)
|
||||
ffi::lua_pushcclosure(state, meta_index_impl, 3);
|
||||
})?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
@@ -425,7 +429,7 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
protect_lua(state, 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, meta_newindex_impl, 2)
|
||||
ffi::lua_pushcclosure(state, meta_newindex_impl, 2);
|
||||
})?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
@@ -480,10 +484,7 @@ where
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
let ud = ffi::lua_newuserdata(
|
||||
state,
|
||||
mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>()),
|
||||
);
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
@@ -494,9 +495,9 @@ where
|
||||
Ok(Err(err)) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
|
||||
let wrapped_error = ud as *mut WrappedError;
|
||||
ptr::write(wrapped_error, WrappedError(err));
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
let wrapped_error = ud as *mut WrappedFailure;
|
||||
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
// Convert to CallbackError and attach traceback
|
||||
@@ -508,15 +509,17 @@ where
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
let cause = Arc::new((*wrapped_error).0.clone());
|
||||
(*wrapped_error).0 = Error::CallbackError { traceback, cause };
|
||||
if let WrappedFailure::Error(ref mut err) = *wrapped_error {
|
||||
let cause = Arc::new(err.clone());
|
||||
*err = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||
get_gc_metatable_for::<WrappedPanic>(state);
|
||||
ptr::write(ud as *mut WrappedFailure, WrappedFailure::Panic(Some(p)));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
@@ -530,9 +533,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if get_gc_userdata::<WrappedError>(state, -1).is_null()
|
||||
&& get_gc_userdata::<WrappedPanic>(state, -1).is_null()
|
||||
{
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, s, 1);
|
||||
@@ -558,7 +559,9 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_insert(state, 1);
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
if !get_gc_userdata::<WrappedPanic>(state, -1).is_null() {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
{
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
@@ -572,7 +575,9 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
if !get_gc_userdata::<WrappedPanic>(state, -1).is_null() {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
{
|
||||
1
|
||||
} else {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
@@ -600,7 +605,9 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_insert(state, 2);
|
||||
ffi::lua_gettop(state) - 1
|
||||
} else {
|
||||
if !get_gc_userdata::<WrappedPanic>(state, -1).is_null() {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
{
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
@@ -632,26 +639,9 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes a WrappedError to the top of the stack.
|
||||
// Uses 3 stack spaces and does not call checkstack.
|
||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
||||
push_gc_userdata::<WrappedError>(state, WrappedError(err))
|
||||
}
|
||||
|
||||
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
||||
// otherwise returns null.
|
||||
// Uses 2 stack spaces and does not call checkstack.
|
||||
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
||||
let ud = get_gc_userdata::<WrappedError>(state, index);
|
||||
if ud.is_null() {
|
||||
return ptr::null();
|
||||
}
|
||||
&(*ud).0
|
||||
}
|
||||
|
||||
// Initialize the internal (with __gc method) metatable for a type T.
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub unsafe fn init_gc_metatable_for<T: Any>(
|
||||
pub unsafe fn init_gc_metatable<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
@@ -681,13 +671,13 @@ pub unsafe fn init_gc_metatable_for<T: Any>(
|
||||
}
|
||||
|
||||
protect_lua(state, 1, 0, |state| {
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void)
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = {
|
||||
let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||
@@ -697,7 +687,6 @@ pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||
}
|
||||
|
||||
// Initialize the error, panic, and destructed userdata metatables.
|
||||
// Returns address of WrappedError and WrappedPanic metatables in Lua registry.
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 7)?;
|
||||
|
||||
@@ -707,63 +696,67 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1).as_ref() {
|
||||
Some(WrappedFailure::Error(error)) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
(*err_buf).clear();
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
// Find first two sources that caused the error
|
||||
let mut source1 = error.source();
|
||||
let mut source0 = source1.and_then(|s| s.source());
|
||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||
source1 = source0;
|
||||
source0 = Some(source);
|
||||
}
|
||||
match (source1, source0) {
|
||||
(_, Some(error0)) if error0.to_string().contains("\nstack traceback:\n") => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
(*err_buf).clear();
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
// Find first two sources that caused the error
|
||||
let mut source1 = error.source();
|
||||
let mut source0 = source1.and_then(|s| s.source());
|
||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||
source1 = source0;
|
||||
source0 = Some(source);
|
||||
}
|
||||
(Some(error1), Some(error0)) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
let s = error1.to_string();
|
||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||
let _ = write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||
match (source1, source0) {
|
||||
(_, Some(error0))
|
||||
if error0.to_string().contains("\nstack traceback:\n") =>
|
||||
{
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
}
|
||||
(Some(error1), Some(error0)) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
let s = error1.to_string();
|
||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||
let _ =
|
||||
write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||
}
|
||||
}
|
||||
(Some(error1), None) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
(Some(error1), None) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||
}
|
||||
_ => {}
|
||||
Ok(err_buf)
|
||||
}
|
||||
Ok(err_buf)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
(*err_buf).clear();
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
if let Some(msg) = p.downcast_ref::<&str>() {
|
||||
if let Some(msg) = panic.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else if let Some(msg) = p.downcast_ref::<String>() {
|
||||
} else if let Some(msg) = panic.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else {
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
Ok(err_buf)
|
||||
} else {
|
||||
Err(Error::PreviouslyResumedPanic)
|
||||
}
|
||||
} else {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
|
||||
_ => {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
}?;
|
||||
|
||||
push_string(state, &*err_buf)?;
|
||||
@@ -773,15 +766,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
})
|
||||
}
|
||||
|
||||
init_gc_metatable_for::<WrappedError>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
rawset_field(state, -2, "__tostring")
|
||||
}),
|
||||
)?;
|
||||
|
||||
init_gc_metatable_for::<WrappedPanic>(
|
||||
init_gc_metatable::<WrappedFailure>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
@@ -840,24 +825,26 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
protect_lua(state, 1, 0, |state| {
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key)
|
||||
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
|
||||
})?;
|
||||
|
||||
// Create error print buffer
|
||||
init_gc_metatable_for::<String>(state, None)?;
|
||||
init_gc_metatable::<String>(state, None)?;
|
||||
push_gc_userdata(state, String::new())?;
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
protect_lua(state, 1, 0, |state| {
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedError(pub Error);
|
||||
pub(crate) struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||
pub(crate) enum WrappedFailure {
|
||||
Error(Error),
|
||||
Panic(Option<Box<dyn Any + Send + 'static>>),
|
||||
}
|
||||
|
||||
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||
// panicking.
|
||||
|
||||
@@ -14,3 +14,50 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<isize>`
|
||||
= note: required because it appears within the type `Cell<isize>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
@@ -16,3 +16,54 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<isize>`
|
||||
= note: required because it appears within the type `Cell<isize>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
@@ -2,7 +2,7 @@ use mlua::{Lua, UserData};
|
||||
|
||||
fn main() {
|
||||
struct MyUserData<'a>(&'a mut i32);
|
||||
impl<'a> UserData for MyUserData<'a> {};
|
||||
impl<'a> UserData for MyUserData<'a> {}
|
||||
|
||||
let mut i = 1;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use mlua::{Lua, UserData};
|
||||
fn main() {
|
||||
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
||||
struct MyUserData<'a>(&'a i32);
|
||||
impl<'a> UserData for MyUserData<'a> {};
|
||||
impl<'a> UserData for MyUserData<'a> {}
|
||||
|
||||
let igood = 1;
|
||||
|
||||
|
||||
+14
-8
@@ -12,7 +12,7 @@ fn test_conv_vec() -> Result<()> {
|
||||
let v = vec![1, 2, 3];
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Vec<i32> = lua.globals().get("v")?;
|
||||
assert!(v == v2);
|
||||
assert_eq!(v, v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -24,7 +24,7 @@ fn test_conv_hashmap() -> Result<()> {
|
||||
let map = hashmap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: HashMap<String, String> = lua.globals().get("map")?;
|
||||
assert!(map == map2);
|
||||
assert_eq!(map, map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -36,7 +36,10 @@ fn test_conv_hashset() -> Result<()> {
|
||||
let set = hashset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: HashSet<String> = lua.globals().get("set")?;
|
||||
assert!(set == set2);
|
||||
assert_eq!(set, set2);
|
||||
|
||||
let set3 = lua.load(r#"{"a", "b", "c"}"#).eval::<HashSet<String>>()?;
|
||||
assert_eq!(set3, hashset! { "a".into(), "b".into(), "c".into() });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -48,7 +51,7 @@ fn test_conv_btreemap() -> Result<()> {
|
||||
let map = btreemap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: BTreeMap<String, String> = lua.globals().get("map")?;
|
||||
assert!(map == map2);
|
||||
assert_eq!(map, map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -60,7 +63,10 @@ fn test_conv_btreeset() -> Result<()> {
|
||||
let set = btreeset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: BTreeSet<String> = lua.globals().get("set")?;
|
||||
assert!(set == set2);
|
||||
assert_eq!(set, set2);
|
||||
|
||||
let set3 = lua.load(r#"{"a", "b", "c"}"#).eval::<BTreeSet<String>>()?;
|
||||
assert_eq!(set3, btreeset! { "a".into(), "b".into(), "c".into() });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -89,7 +95,7 @@ fn test_conv_cow() -> Result<()> {
|
||||
let s = Cow::from("hello");
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: String = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
assert_eq!(s, s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -101,7 +107,7 @@ fn test_conv_boxed_str() -> Result<()> {
|
||||
let s = String::from("hello").into_boxed_str();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: Box<str> = lua.globals().get("s")?;
|
||||
assert!(s == s2);
|
||||
assert_eq!(s, s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -113,7 +119,7 @@ fn test_conv_boxed_slice() -> Result<()> {
|
||||
let v = vec![1, 2, 3].into_boxed_slice();
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Box<[i32]> = lua.globals().get("v")?;
|
||||
assert!(v == v2);
|
||||
assert_eq!(v, v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -76,6 +76,23 @@ fn test_rust_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_c_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
unsafe extern "C" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
lua.globals().set("c_function", true).unwrap();
|
||||
0
|
||||
}
|
||||
|
||||
let func = unsafe { lua.create_c_function(c_function)? };
|
||||
func.call(())?;
|
||||
assert_eq!(lua.globals().get::<_, bool>("c_function")?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dump() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
@@ -1086,3 +1087,30 @@ fn test_jit_version() -> Result<()> {
|
||||
.contains("LuaJIT"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_from_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Arc::new(AtomicU32::new(0));
|
||||
let i2 = i.clone();
|
||||
let func = lua.create_function(move |lua, modname: String| {
|
||||
i2.fetch_add(1, Ordering::Relaxed);
|
||||
let t = lua.create_table()?;
|
||||
t.set("__name", modname)?;
|
||||
Ok(t)
|
||||
})?;
|
||||
|
||||
let t: Table = lua.load_from_function("my_module", func.clone())?;
|
||||
assert_eq!(t.get::<_, String>("__name")?, "my_module");
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let _: Value = lua.load_from_function("my_module", func)?;
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let func_nil = lua.create_function(move |_, _: String| Ok(Value::Nil))?;
|
||||
let v: Value = lua.load_from_function("my_module2", func_nil)?;
|
||||
assert_eq!(v, Value::Boolean(true));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user