Skip to content

Commit

Permalink
Merge branch 'mlua-rs:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
rise0chen authored Jan 7, 2025
2 parents 09dfc9b + cd4091f commit d7d87bd
Show file tree
Hide file tree
Showing 19 changed files with 160 additions and 62 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## v0.10.2 (Dec 1st, 2024)

- Switch proc-macro-error to proc-macro-error2 (#493)
- Do not allow Lua to run GC finalizers on ref thread (#491)
- Fix chunks loading in Luau when memory limit is enforced (#488)
- Added `String::wrap` method to wrap arbitrary `AsRef<[u8]>` into `impl IntoLua`
- Better FreeBSD/OpenBSD support (thanks to cos)
- Delay "any" userdata metatable creation until first instance is created (#482)
- Reduce amount of generated code for `UserData` (less generics)

## v0.10.1 (Nov 9th, 2024)

- Minimal Luau updated to 0.650
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.10.1" # remember to update mlua_derive
version = "0.10.2" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <[email protected]>", "kyren <[email protected]>"]
rust-version = "1.79.0"
edition = "2021"
Expand Down Expand Up @@ -46,7 +46,7 @@ anyhow = ["dep:anyhow", "error-send"]
userdata-wrappers = []

[dependencies]
mlua_derive = { version = "=0.10.0", optional = true, path = "mlua_derive" }
mlua_derive = { version = "=0.10.1", optional = true, path = "mlua_derive" }
bstr = { version = "1.0", features = ["std"], default-features = false }
either = "1.0"
num-traits = { version = "0.2.14" }
Expand All @@ -58,7 +58,7 @@ serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", features = ["arc_lock"] }
anyhow = { version = "1.0", optional = true }

ffi = { package = "mlua-sys", version = "0.6.5", path = "mlua-sys" }
ffi = { package = "mlua-sys", version = "0.6.6", path = "mlua-sys" }

[target.'cfg(unix)'.dependencies]
libloading = { version = "0.8", optional = true }
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Add to `Cargo.toml` :

``` toml
[dependencies]
mlua = { version = "0.10.1", features = ["lua54", "vendored"] }
mlua = { version = "0.10.2", features = ["lua54", "vendored"] }
```

`main.rs`
Expand Down Expand Up @@ -168,7 +168,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]

[dependencies]
mlua = { version = "0.10.1", features = ["lua54", "module"] }
mlua = { version = "0.10.2", features = ["lua54", "module"] }
```

`lib.rs` :
Expand Down
2 changes: 1 addition & 1 deletion mlua-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mlua-sys"
version = "0.6.5"
version = "0.6.6"
authors = ["Aleksandr Orlenko <[email protected]>"]
rust-version = "1.71"
edition = "2021"
Expand Down
15 changes: 13 additions & 2 deletions mlua-sys/src/luau/compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,16 @@ pub unsafe fn luaL_loadbufferenv(
mut size: usize,
name: *const c_char,
mode: *const c_char,
env: c_int,
mut env: c_int,
) -> c_int {
extern "C" {
fn free(p: *mut c_void);
}

unsafe extern "C-unwind" fn data_dtor(data: *mut c_void) {
free(*(data as *mut *mut c_char) as *mut c_void);
}

let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
if !mode.is_null() {
let modeb = CStr::from_ptr(mode).to_bytes();
Expand All @@ -345,9 +349,16 @@ pub unsafe fn luaL_loadbufferenv(
}

if chunk_is_text {
if env < 0 {
env -= 1;
}
let data_ud = lua_newuserdatadtor(L, mem::size_of::<*mut c_char>(), data_dtor) as *mut *mut c_char;
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
ptr::write(data_ud, data);
// By deferring the `free(data)` to the userdata destructor, we ensure that
// even if `luau_load` throws an error, the `data` is still released.
let ok = luau_load(L, name, data, size, env) == 0;
free(data as *mut c_void);
lua_replace(L, -2); // replace data with the result
if !ok {
return LUA_ERRSYNTAX;
}
Expand Down
6 changes: 3 additions & 3 deletions mlua_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.10.0"
version = "0.10.1"
authors = ["Aleksandr Orlenko <[email protected]>"]
edition = "2021"
description = "Procedural macros for the mlua crate."
Expand All @@ -12,12 +12,12 @@ license = "MIT"
proc-macro = true

[features]
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
macros = ["proc-macro-error2", "itertools", "regex", "once_cell"]

[dependencies]
quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error = { version = "1.0", optional = true }
proc-macro-error2 = { version = "2.0.1", optional = true }
syn = { version = "2.0", features = ["full"] }
itertools = { version = "0.13", optional = true }
regex = { version = "1.4", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion mlua_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use syn::{parse_macro_input, ItemFn, LitStr, Result};
#[cfg(feature = "macros")]
use {
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
proc_macro_error::proc_macro_error,
proc_macro_error2::proc_macro_error,
};

#[derive(Default)]
Expand Down
2 changes: 1 addition & 1 deletion mlua_derive/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
let (start, end) = match parse_pos(span) {
Some(v) => v,
None => proc_macro_error::abort_call_site!("Cannot retrieve span information; please use nightly"),
None => proc_macro_error2::abort_call_site!("Cannot retrieve span information; please use nightly"),
};
(Pos::new(1, start), Pos::new(1, end))
}
Expand Down
4 changes: 2 additions & 2 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,8 +1043,8 @@ impl Lua {
let state = lua.state();
unsafe {
if lua.unlikely_memory_error() {
crate::util::push_buffer(lua.ref_thread(), buf.as_ref(), false)?;
return Ok(Buffer(lua.pop_ref_thread()));
crate::util::push_buffer(state, buf.as_ref(), false)?;
return Ok(Buffer(lua.pop_ref()));
}

let _sg = StackGuard::new(state);
Expand Down
6 changes: 4 additions & 2 deletions src/state/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use super::{Lua, WeakLua};
// Unique key to store `ExtraData` in the registry
static EXTRA_REGISTRY_KEY: u8 = 0;

const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
const REF_STACK_RESERVE: c_int = 1;

/// Data associated with the Lua state.
Expand Down Expand Up @@ -60,6 +60,7 @@ pub(crate) struct ExtraData {

// Pool of `WrappedFailure` enums in the ref thread (as userdata)
pub(super) wrapped_failure_pool: Vec<c_int>,
pub(super) wrapped_failure_top: usize,
// Pool of `Thread`s (coroutines) for async execution
#[cfg(feature = "async")]
pub(super) thread_pool: Vec<c_int>,
Expand Down Expand Up @@ -160,7 +161,8 @@ impl ExtraData {
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
ref_stack_top: ffi::lua_gettop(ref_thread),
ref_free: Vec::new(),
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY),
wrapped_failure_top: 0,
#[cfg(feature = "async")]
thread_pool: Vec::new(),
wrapped_failure_mt_ptr,
Expand Down
18 changes: 11 additions & 7 deletions src/state/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,10 @@ impl RawLua {
Some(ChunkMode::Text) => cstr!("t"),
None => cstr!("bt"),
};
let status = if cfg!(not(feature = "luau")) || self.unlikely_memory_error() {
let status = if self.unlikely_memory_error() {
self.load_chunk_inner(state, name, env, mode, source)
} else {
// Only Luau can trigger an exception during chunk loading
// Luau and Lua 5.2 can trigger an exception during chunk loading
protect_lua!(state, 0, 1, |state| {
self.load_chunk_inner(state, name, env, mode, source)
})?
Expand Down Expand Up @@ -427,8 +427,8 @@ impl RawLua {
pub(crate) unsafe fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
let state = self.state();
if self.unlikely_memory_error() {
push_string(self.ref_thread(), s.as_ref(), false)?;
return Ok(String(self.pop_ref_thread()));
push_string(state, s.as_ref(), false)?;
return Ok(String(self.pop_ref()));
}

let _sg = StackGuard::new(state);
Expand All @@ -439,12 +439,12 @@ impl RawLua {

/// See [`Lua::create_table_with_capacity`]
pub(crate) unsafe fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
let state = self.state();
if self.unlikely_memory_error() {
push_table(self.ref_thread(), narr, nrec, false)?;
return Ok(Table(self.pop_ref_thread()));
push_table(state, narr, nrec, false)?;
return Ok(Table(self.pop_ref()));
}

let state = self.state();
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
push_table(state, narr, nrec, true)?;
Expand Down Expand Up @@ -729,6 +729,10 @@ impl RawLua {

pub(crate) unsafe fn drop_ref(&self, vref: &ValueRef) {
let ref_thread = self.ref_thread();
mlua_debug_assert!(
ffi::lua_gettop(ref_thread) >= vref.index,
"GC finalizer is not allowed in ref_thread"
);
ffi::lua_pushnil(ref_thread);
ffi::lua_replace(ref_thread, vref.index);
(*self.extra.get()).ref_free.push(vref.index);
Expand Down
58 changes: 23 additions & 35 deletions src/state/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use crate::error::{Error, Result};
use crate::state::{ExtraData, RawLua};
use crate::util::{self, get_internal_metatable, WrappedFailure};

const WRAPPED_FAILURE_POOL_SIZE: usize = 64;

pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);

impl<'a> StateGuard<'a> {
Expand Down Expand Up @@ -42,39 +40,40 @@ where

enum PreallocatedFailure {
New(*mut WrappedFailure),
Existing(i32),
Reserved,
}

impl PreallocatedFailure {
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
match (*extra).wrapped_failure_pool.pop() {
Some(index) => PreallocatedFailure::Existing(index),
None => {
// We need to check stack for Luau in case when callback is called from interrupt
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
// Place it to the beginning of the stack
let ud = WrappedFailure::new_userdata(state);
ffi::lua_insert(state, 1);
PreallocatedFailure::New(ud)
}
if (*extra).wrapped_failure_top > 0 {
(*extra).wrapped_failure_top -= 1;
return PreallocatedFailure::Reserved;
}

// We need to check stack for Luau in case when callback is called from interrupt
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
// Place it to the beginning of the stack
let ud = WrappedFailure::new_userdata(state);
ffi::lua_insert(state, 1);
PreallocatedFailure::New(ud)
}

#[cold]
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
let ref_thread = (*extra).ref_thread;
match *self {
PreallocatedFailure::New(ud) => {
ffi::lua_settop(state, 1);
ud
}
PreallocatedFailure::Existing(index) => {
PreallocatedFailure::Reserved => {
let index = (*extra).wrapped_failure_pool.pop().unwrap();
ffi::lua_settop(state, 0);
#[cfg(feature = "luau")]
ffi::lua_rawcheckstack(state, 2);
ffi::lua_pushvalue(ref_thread, index);
ffi::lua_xmove(ref_thread, state, 1);
ffi::lua_xpush(ref_thread, state, index);
ffi::lua_pushnil(ref_thread);
ffi::lua_replace(ref_thread, index);
(*extra).ref_free.push(index);
Expand All @@ -87,24 +86,13 @@ where
let ref_thread = (*extra).ref_thread;
match self {
PreallocatedFailure::New(_) => {
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
ffi::lua_rotate(state, 1, -1);
ffi::lua_xmove(state, ref_thread, 1);
let index = ref_stack_pop(extra);
(*extra).wrapped_failure_pool.push(index);
} else {
ffi::lua_remove(state, 1);
}
}
PreallocatedFailure::Existing(index) => {
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
(*extra).wrapped_failure_pool.push(index);
} else {
ffi::lua_pushnil(ref_thread);
ffi::lua_replace(ref_thread, index);
(*extra).ref_free.push(index);
}
ffi::lua_rotate(state, 1, -1);
ffi::lua_xmove(state, ref_thread, 1);
let index = ref_stack_pop(extra);
(*extra).wrapped_failure_pool.push(index);
(*extra).wrapped_failure_top += 1;
}
PreallocatedFailure::Reserved => (*extra).wrapped_failure_top += 1,
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,24 @@ impl Table {
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
}

/// Controls `safeenv` attribute on the table.
///
/// This a special flag that activates some performance optimizations for environment tables.
/// In particular, it controls:
/// - Optimization of import resolution (cache values of constant keys).
/// - Fast-path for built-in iteration with pairs/ipairs.
/// - Fast-path for some built-in functions (fastcall).
///
/// For `safeenv` environments, monkey patching or modifying values may not work as expected.
///
/// Requires `feature = "luau"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_safeenv(&self, enabled: bool) {
let lua = self.0.lua.lock();
unsafe { ffi::lua_setsafeenv(lua.ref_thread(), self.0.index, enabled as _) };
}

/// Converts this table to a generic C pointer.
///
/// Different tables will give different pointers.
Expand Down
2 changes: 1 addition & 1 deletion src/types/value_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::os::raw::{c_int, c_void};
use crate::state::{RawLua, WeakLua};

/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
pub(crate) struct ValueRef {
pub struct ValueRef {
pub(crate) lua: WeakLua,
pub(crate) index: c_int,
pub(crate) drop: bool,
Expand Down
3 changes: 1 addition & 2 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ pub enum Value {
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Box<Error>),
/// Any other value not known to mlua (eg. LuaJIT CData).
#[allow(private_interfaces)]
Other(ValueRef),
Other(#[doc(hidden)] ValueRef),
}

pub use self::Value::Nil;
Expand Down
12 changes: 12 additions & 0 deletions tests/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,21 @@ fn test_function_calls() -> Result<()> {

let output = output.lock().unwrap();
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
#[cfg(not(force_memory_limit))]
assert_eq!(*output, vec![(None, "main"), (Some("len".to_string()), "Lua")]);
#[cfg(force_memory_limit)]
assert_eq!(
*output,
vec![(None, "C"), (None, "main"), (Some("len".to_string()), "Lua")]
);
} else {
#[cfg(not(force_memory_limit))]
assert_eq!(*output, vec![(None, "main"), (Some("len".to_string()), "C")]);
#[cfg(force_memory_limit)]
assert_eq!(
*output,
vec![(None, "C"), (None, "main"), (Some("len".to_string()), "C")]
);
}

Ok(())
Expand Down
Loading

0 comments on commit d7d87bd

Please sign in to comment.