Compare commits

..

19 Commits

Author SHA1 Message Date
Alex Orlenko c85616137a v0.7.4 2022-03-01 19:59:49 +00:00
Alex Orlenko f52d106a82 Fix "find_dummy" compile error if no lua feature selected 2022-03-01 19:59:42 +00:00
Alex Orlenko 10826a7e67 Update create_registry_value doc 2022-02-15 19:45:37 +00:00
Alex Orlenko 18c3255c90 Optimize Lua::create_registry_value by reusing previously expired registry keys. 2022-02-14 21:20:57 +00:00
Alex Orlenko 6190427f37 Add Lua::replace_registry_value 2022-02-14 20:51:24 +00:00
Alex Orlenko 9a5a341e44 Recognize LuaJIT TCDATA type to generate correct panic message.
Relates to #127
Should be fixed in a next major release by adding support of TCDATA type.
2022-02-12 18:40:18 +00:00
Alex Orlenko dd91ebfbe5 Grow/check Lua stack in unpack 2022-02-12 17:10:43 +00:00
Alex Orlenko f9fe869b76 Optimize async calls:
Rewrite "unpack" function using C api rather than high level abstraction.
2022-01-29 12:39:30 +00:00
Alex Orlenko 6e4033abba Fix tests for Lua 5.4.4 2022-01-29 12:36:09 +00:00
Alex Orlenko c213a85ef0 v0.7.3 2022-01-19 18:12:45 +00:00
Alex Orlenko 4963cc1b8b Fix cross compilation (bug introduced in 84a174c) 2022-01-19 17:42:32 +00:00
Alex Orlenko 418e8fba32 v0.7.2 2022-01-17 11:12:46 +00:00
Alex Orlenko 08a7e30820 Update README 2022-01-17 11:11:56 +00:00
Alex Orlenko 19676bda40 Update CHANGELOG 2022-01-16 23:50:26 +00:00
Alex Orlenko 5a06778fbc Always restore original Lua state after creating Future in async call.
Fixes #121
2022-01-16 20:57:43 +00:00
Alex Orlenko e33bdddc7a Pass Box wrapped pointer to allocator fn instead of reference 2022-01-08 23:06:01 +00:00
Alex Orlenko cfb5d3fd45 Fix clippy warnings 2021-12-28 12:23:06 +00:00
Alex Orlenko 84a174c94d Allow pkg-config to omit include paths if they equals to standard.
See #114
2021-12-28 12:02:02 +00:00
Alex Orlenko 888b2bbf8d Refactor build/find_normal.rs to include error messages instead of unwrap() 2021-12-28 10:26:12 +00:00
17 changed files with 205 additions and 83 deletions
+16
View File
@@ -1,3 +1,19 @@
## v0.7.4
- Improved `Lua::create_registry_value` to reuse previously expired registry keys.
No need to call `Lua::expire_registry_values` when creating/dropping registry values.
- Added `Lua::replace_registry_value` to change value of an existing Registry Key
- Async calls optimization
## v0.7.3
- Fixed cross-compilation issue (introduced in 84a174c)
## v0.7.2
- Allow `pkg-config` to omit include paths if they equals to standard (#114).
- Various bugfixes (eg. #121)
## v0.7.1
- Fixed traceback generation for errors (#112)
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.7.1" # remember to update html_root_url and mlua_derive
version = "0.7.4" # 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"
+1 -1
View File
@@ -16,7 +16,7 @@
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
Started as [rlua] fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
+1 -1
View File
@@ -1,5 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> PathBuf {
pub fn probe_lua() -> Option<PathBuf> {
unreachable!()
}
+23 -51
View File
@@ -10,7 +10,7 @@ fn get_env_var(name: &str) -> String {
}
}
pub fn probe_lua() -> PathBuf {
pub fn probe_lua() -> Option<PathBuf> {
let include_dir = get_env_var("LUA_INC");
let lib_dir = get_env_var("LUA_LIB");
let lua_lib = get_env_var("LUA_LIB_NAME");
@@ -38,73 +38,42 @@ pub fn probe_lua() -> PathBuf {
println!("cargo:rustc-link-search=native={}", lib_dir);
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
}
return PathBuf::from(include_dir);
return Some(PathBuf::from(include_dir));
}
// Find using `pkg-config`
#[cfg(feature = "lua54")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.4"), Bound::Excluded("5.5")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.4");
}
lua.unwrap().include_paths[0].clone()
}
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", "lua5.4", "5.4");
#[cfg(feature = "lua53")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.3");
}
lua.unwrap().include_paths[0].clone()
}
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", "lua5.3", "5.3");
#[cfg(feature = "lua52")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.2");
}
lua.unwrap().include_paths[0].clone()
}
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", "lua5.2", "5.2");
#[cfg(feature = "lua51")]
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", "lua5.1", "5.1");
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "lua51"
))]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
.cargo_metadata(need_lua_lib)
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new()
.cargo_metadata(need_lua_lib)
.probe("lua5.1");
.probe(alt_probe);
}
lua.unwrap().include_paths[0].clone()
lua.expect(&format!("cannot find Lua {} using `pkg-config`", ver))
.include_paths
.get(0)
.cloned()
}
#[cfg(feature = "luajit")]
@@ -114,6 +83,9 @@ pub fn probe_lua() -> PathBuf {
.cargo_metadata(need_lua_lib)
.probe("luajit");
lua.unwrap().include_paths[0].clone()
lua.expect("cannot find LuaJIT using `pkg-config`")
.include_paths
.get(0)
.cloned()
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
use std::path::PathBuf;
pub fn probe_lua() -> PathBuf {
pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "lua54")]
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
#[cfg(feature = "lua53")]
@@ -21,5 +21,5 @@ pub fn probe_lua() -> PathBuf {
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata();
artifacts.include_dir().to_owned()
Some(artifacts.include_dir().to_owned())
}
+7 -3
View File
@@ -68,11 +68,14 @@ impl CommandExt for Command {
}
}
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
// `include_path` is optional as Lua headers can be also found in compiler standard paths
fn build_glue(include_path: Option<impl AsRef<Path>>) {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut config = cc::Build::new();
config.include(include_path);
if let Some(include_path) = include_path {
config.include(include_path.as_ref());
}
// Compile and run glue.c
let glue = build_dir.join("glue");
@@ -245,9 +248,10 @@ fn main() {
let include_dir = find::probe_lua();
if env::var("TARGET").unwrap() != env::var("HOST").unwrap() {
// The `probe_lua` call above is still needed here
generate_glue().unwrap();
} else {
build_glue(&include_dir);
build_glue(include_dir);
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
}
+2 -3
View File
@@ -175,8 +175,7 @@ impl Tokens {
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
Tokens(
tt.into_iter()
.map(Tokens::from)
.flatten()
.flat_map(Tokens::from)
.peekable()
.batching(|iter| {
// Find variable tokens
@@ -217,7 +216,7 @@ impl From<TokenTree> for Tokens {
vec![Token::new_delim(b, tt.clone(), true)]
.into_iter()
.chain(g.stream().into_iter().map(Tokens::from).flatten())
.chain(g.stream().into_iter().flat_map(Tokens::from))
.chain(vec![Token::new_delim(e, tt, false)])
.collect()
}
+4
View File
@@ -101,6 +101,10 @@ pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
// Type produced by LuaJIT FFI module
#[cfg(feature = "luajit")]
pub const LUA_TCDATA: c_int = 10;
#[cfg(feature = "lua54")]
pub const LUA_NUMTYPES: c_int = 9;
#[cfg(any(feature = "lua53", feature = "lua52"))]
+3
View File
@@ -236,6 +236,9 @@ pub use self::lua::LUA_ERRGCMM;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
#[cfg(feature = "luajit")]
pub use self::lua::LUA_TCDATA;
// constants from lauxlib.h
pub use self::lauxlib::{LUA_ERRFILE, LUA_NOREF, LUA_REFNIL};
+1 -1
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.7.1")]
#![doc(html_root_url = "https://docs.rs/mlua/0.7.4")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
+97 -18
View File
@@ -83,7 +83,7 @@ struct ExtraData {
app_data: RefCell<HashMap<TypeId, Box<dyn Any + Send>>>,
libs: StdLib,
mem_info: Option<Box<MemoryInfo>>,
mem_info: Option<ptr::NonNull<MemoryInfo>>,
safe: bool, // Same as in the Lua struct
ref_thread: *mut ffi::lua_State,
@@ -180,6 +180,7 @@ impl LuaOptions {
/// Sets [`catch_rust_panics`] option.
///
/// [`catch_rust_panics`]: #structfield.catch_rust_panics
#[must_use]
pub const fn catch_rust_panics(mut self, enabled: bool) -> Self {
self.catch_rust_panics = enabled;
self
@@ -190,6 +191,7 @@ impl LuaOptions {
/// [`thread_cache_size`]: #structfield.thread_cache_size
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[must_use]
pub const fn thread_cache_size(mut self, size: usize) -> Self {
self.thread_cache_size = size;
self
@@ -242,6 +244,9 @@ impl Drop for Lua {
impl Drop for ExtraData {
fn drop(&mut self) {
*mlua_expect!(self.registry_unref_list.lock(), "unref list poisoned") = None;
if let Some(mem_info) = self.mem_info {
drop(unsafe { Box::from_raw(mem_info.as_ptr()) });
}
}
}
@@ -386,13 +391,13 @@ impl Lua {
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
let mut mem_info = Box::new(MemoryInfo {
let mem_info = Box::into_raw(Box::new(MemoryInfo {
used_memory: 0,
memory_limit: 0,
});
}));
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
let state = ffi::lua_newstate(allocator, &mut *mem_info as *mut MemoryInfo as *mut c_void);
let state = ffi::lua_newstate(allocator, mem_info as *mut c_void);
#[cfg(any(feature = "lua51", feature = "luajit"))]
let state = ffi::luaL_newstate();
@@ -406,7 +411,7 @@ impl Lua {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
extra.mem_info = Some(mem_info);
extra.mem_info = ptr::NonNull::new(mem_info);
}
mlua_expect!(
@@ -918,7 +923,7 @@ impl Lua {
pub fn used_memory(&self) -> usize {
unsafe {
let state = self.main_state.unwrap_or(self.state);
match &(*self.extra.get()).mem_info {
match (*self.extra.get()).mem_info.map(|x| x.as_ref()) {
Some(mem_info) => mem_info.used_memory as usize,
None => {
// Get data from the Lua GC
@@ -942,7 +947,7 @@ impl Lua {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
pub fn set_memory_limit(&self, memory_limit: usize) -> Result<usize> {
unsafe {
match &mut (*self.extra.get()).mem_info {
match (*self.extra.get()).mem_info.map(|mut x| x.as_mut()) {
Some(mem_info) => {
let prev_limit = mem_info.memory_limit as usize;
mem_info.memory_limit = memory_limit as isize;
@@ -1703,11 +1708,12 @@ impl Lua {
/// Place a value in the Lua registry with an auto-generated key.
///
/// This value will be available to rust from all `Lua` instances which share the same main
/// This value will be available to Rust from all `Lua` instances which share the same main
/// state.
///
/// Be warned, garbage collection of values held inside the registry is not automatic, see
/// [`RegistryKey`] for more details.
/// However, dropped [`RegistryKey`]s automatically reused to store new values.
///
/// [`RegistryKey`]: crate::RegistryKey
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
@@ -1716,14 +1722,29 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 4)?;
let unref_list = (*self.extra.get()).registry_unref_list.clone();
self.push_value(t)?;
// Try to reuse previously allocated RegistryKey
let unref_list2 = unref_list.clone();
let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned");
if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) {
// It must be safe to replace the value without triggering memory error
ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
return Ok(RegistryKey {
registry_id,
unref_list,
});
}
// Allocate a new RegistryKey
let registry_id = protect_lua!(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
Ok(RegistryKey {
registry_id,
unref_list: (*self.extra.get()).registry_unref_list.clone(),
unref_list,
})
}
}
@@ -1772,6 +1793,37 @@ impl Lua {
Ok(())
}
/// Replaces a value in the Lua registry by its `RegistryKey`.
///
/// See [`create_registry_value`] for more details.
///
/// [`create_registry_value`]: #method.create_registry_value
pub fn replace_registry_value<'lua, T: ToLua<'lua>>(
&'lua self,
key: &RegistryKey,
t: T,
) -> Result<()> {
if !self.owns_registry_value(key) {
return Err(Error::MismatchedRegistryKey);
}
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 2)?;
self.push_value(t)?;
// It must be safe to replace the value without triggering memory error
ffi::lua_rawseti(
self.state,
ffi::LUA_REGISTRYINDEX,
key.registry_id as Integer,
);
Ok(())
}
}
/// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying
/// main state with this `Lua` instance.
///
@@ -1983,6 +2035,13 @@ impl Lua {
ffi::LUA_TTHREAD => Value::Thread(Thread(self.pop_ref())),
#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
ffi::lua_pop(state, 1);
// TODO: Fix this in a next major release
panic!("cdata objects cannot be handled by mlua yet");
}
_ => mlua_panic!("LUA_TNONE in pop_value"),
}
}
@@ -2278,6 +2337,22 @@ impl Lua {
}
}
struct StateGuard(*mut Lua, *mut ffi::lua_State);
impl StateGuard {
unsafe fn new(lua: *mut Lua, state: *mut ffi::lua_State) -> Self {
let orig_state = (*lua).state;
(*lua).state = state;
Self(lua, orig_state)
}
}
impl Drop for StateGuard {
fn drop(&mut self) {
unsafe { (*self.0).state = self.1 }
}
}
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
let extra = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
ffi::LUA_TUSERDATA => {
@@ -2299,7 +2374,7 @@ impl Lua {
}
let lua = &mut (*upvalue).lua;
lua.state = state;
let _guard = StateGuard::new(lua, state);
let mut args = MultiValue::new_or_cached(lua);
args.reserve(nargs as usize);
@@ -2379,19 +2454,23 @@ impl Lua {
Function(self.pop_ref())
};
unsafe extern "C" fn unpack(state: *mut ffi::lua_State) -> c_int {
let len = ffi::lua_tointeger(state, 2);
ffi::luaL_checkstack(state, len as c_int, ptr::null());
for i in 1..=len {
ffi::lua_rawgeti(state, 1, i);
}
len as c_int
}
let coroutine = self.globals().get::<_, Table>("coroutine")?;
let env = self.create_table_with_capacity(0, 4)?;
env.set("get_poll", get_poll)?;
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
env.set(
"unpack",
self.create_function(|lua, (tbl, len): (Table, Integer)| {
let mut values = MultiValue::new_or_cached(lua);
values.refill(tbl.raw_sequence_values_by_len(Some(len)))?;
Ok(values)
})?,
)?;
unsafe {
env.set("unpack", self.create_c_function(unpack)?)?;
}
env.set("pending", {
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut c_void)
})?;
+2
View File
@@ -61,6 +61,7 @@ impl Options {
/// Sets [`deny_unsupported_types`] option.
///
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
#[must_use]
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
self.deny_unsupported_types = enabled;
self
@@ -69,6 +70,7 @@ impl Options {
/// Sets [`deny_recursive_tables`] option.
///
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
#[must_use]
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
self.deny_recursive_tables = enabled;
self
+3
View File
@@ -69,6 +69,7 @@ impl Options {
/// Sets [`set_array_metatable`] option.
///
/// [`set_array_metatable`]: #structfield.set_array_metatable
#[must_use]
pub const fn set_array_metatable(mut self, enabled: bool) -> Self {
self.set_array_metatable = enabled;
self
@@ -77,6 +78,7 @@ impl Options {
/// Sets [`serialize_none_to_null`] option.
///
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
#[must_use]
pub const fn serialize_none_to_null(mut self, enabled: bool) -> Self {
self.serialize_none_to_null = enabled;
self
@@ -85,6 +87,7 @@ impl Options {
/// Sets [`serialize_unit_to_null`] option.
///
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
#[must_use]
pub const fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
self.serialize_unit_to_null = enabled;
self
+1 -1
View File
@@ -455,7 +455,7 @@ impl<'lua> Table<'lua> {
}
}
#[cfg(any(feature = "async", feature = "serialize"))]
#[cfg(any(feature = "serialize"))]
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
self,
len: Option<Integer>,
+32
View File
@@ -800,6 +800,17 @@ fn test_drop_registry_value() -> Result<()> {
Ok(())
}
#[test]
fn test_replace_registry_value() -> Result<()> {
let lua = Lua::new();
let key = lua.create_registry_value::<i32>(42)?;
lua.replace_registry_value(&key, "new value")?;
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
Ok(())
}
#[test]
fn test_lua_registry_hash() -> Result<()> {
let lua = Lua::new();
@@ -1256,3 +1267,24 @@ fn test_warnings() -> Result<()> {
Ok(())
}
#[test]
#[cfg(feature = "luajit")]
#[should_panic]
fn test_luajit_cdata() {
let lua = unsafe { Lua::unsafe_new() };
let _v: Result<Value> = lua
.load(
r#"
local ffi = require("ffi")
ffi.cdef[[
void *malloc(size_t size);
void free(void *ptr);
]]
local ptr = ffi.C.malloc(1)
ffi.C.free(ptr)
return ptr
"#,
)
.eval();
}
+9 -1
View File
@@ -130,7 +130,15 @@ fn test_thread_reset() -> Result<()> {
assert_eq!(thread.status(), ThreadStatus::Error);
assert_eq!(Arc::strong_count(&arc), 2);
assert!(thread.reset(func.clone()).is_err());
assert_eq!(thread.status(), ThreadStatus::Error);
// Reset behavior has changed in Lua v5.4.4
// It's became possible to force reset thread by popping error object
assert!(matches!(
thread.status(),
ThreadStatus::Unresumable | ThreadStatus::Error
));
// Would pass in 5.4.4
// assert!(thread.reset(func.clone()).is_ok());
// assert_eq!(thread.status(), ThreadStatus::Resumable);
}
Ok(())