Compare commits

...

23 Commits

Author SHA1 Message Date
Alex Orlenko 628b70f561 v0.6.2 2021-07-13 20:29:25 +01:00
Alex Orlenko 8a7e546c66 Shrink unsafe block in Lua::load_from_function and update doc 2021-07-13 16:43:00 +01:00
Alex Orlenko 97bd288f56 Add CI tests with enabled address sanitizer
Don't use release mode in tests
Try to use cache in CI
2021-07-13 16:17:29 +01:00
Alex Orlenko 59253410bb Update trybuild messages 2021-07-13 11:59:33 +01:00
Alex Orlenko 5fb7b96704 Replace custom UserDataRef(Mut) with standard Ref(Mut) 2021-07-13 10:53:10 +01:00
Alex Orlenko 17162b1e0b UserData improvements:
1) Optimize `make_userdata` call
2) Refactor `UserDataWrapped` and make it enum
2021-07-11 01:30:17 +01:00
Alex Orlenko 934aa3b65f Forgotten optimization for box_method_mut 2021-07-09 23:25:48 +01:00
Alex Orlenko 3bffe1d7c3 Combine WrappedError and WrappedPanic structs to WrappedFailure enum 2021-07-09 00:05:29 +01:00
Alex Orlenko 582b2c585f Rename init_gc_metatable_for and get_gc_metatable_for 2021-07-08 22:57:54 +01:00
Alex Orlenko 56b6b128b5 Minor fixes/compilation after revert 2021-07-08 22:53:53 +01:00
Alex Orlenko 1731f5d61b Revert "Make protect_lua as a smart macro to choose from C/closure"
This reverts commit 84fe5f7f76.
2021-07-08 18:41:10 +01:00
Alex Orlenko 60822d12d2 Revert "Move away from metatable hashmap cache to direct keys"
This reverts commit adbc9ccc9b.
2021-07-08 18:40:14 +01:00
Alex Orlenko 8ff610529b Don't wrap ExtraData to Arc<Mutex> and use raw pointer instead.
This causes serious performance issues and given that Lua is
single threaded (not Sync) it's safe to use a raw pointer instead.
2021-07-08 17:32:20 +01:00
Alex Orlenko adbc9ccc9b Move away from metatable hashmap cache to direct keys 2021-07-08 15:37:18 +01:00
Alex Orlenko 84fe5f7f76 Make protect_lua as a smart macro to choose from C/closure 2021-07-07 12:54:19 +01:00
Alex Orlenko 1fe583027b Add new functions: lua.load_from_function() and lua.create_c_function()
This should be useful to register embedded C modules to Lua state.
Provides a solution for #61
2021-07-05 00:03:18 +01:00
Alex Orlenko 7b5b78fa3d Add codecov.yml to don't fail on coverage change 2021-06-30 21:52:50 +01:00
Alex Orlenko 41aae83304 Optimize callback creation
Attach only one upvalue to callbacks rather than two.
This leads to less lookup to Lua registry.
2021-06-30 16:50:50 +01:00
Alex Orlenko fc84e8623e v0.6.1 2021-06-28 00:10:07 +01:00
Alex Orlenko 8aae9a7c3e Update chunk! documentation 2021-06-28 00:10:04 +01:00
Alex Orlenko 24bbd62564 Fix converting Lua sequence table to HashSet/BTreeSet 2021-06-25 01:49:09 +01:00
Alex Orlenko 30af919931 Lower once_cell dependency to 1.0 2021-06-25 00:39:10 +01:00
Alex Orlenko 89b5981c59 Fix typos 2021-06-25 00:38:49 +01:00
20 changed files with 710 additions and 525 deletions
+40 -11
View File
@@ -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
View File
@@ -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
View File
@@ -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 }
+17
View File
@@ -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,
+8
View File
@@ -0,0 +1,8 @@
coverage:
status:
patch:
default:
only_pulls: true
project:
default:
only_pulls: true
+12 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+17
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+47
View File
@@ -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]`
+51
View File
@@ -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]`
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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(())
}
+17
View File
@@ -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() };
+28
View File
@@ -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(())
}