Compare commits

...

12 Commits

Author SHA1 Message Date
Alex Orlenko 3516f4c6ca v0.11.2 2025-08-10 00:53:45 +01:00
Alex Orlenko ca73583714 Update CHANGELOG 2025-08-10 00:53:01 +01:00
Alex Orlenko 36560435f7 Add push_into_stack_multi fastpath to Variadic 2025-08-10 00:35:51 +01:00
Alex Orlenko 763c2b2564 Update repl example: don't print newline if no values returned 2025-08-10 00:20:20 +01:00
Alex Orlenko bafdb6138c Update dependencies 2025-08-10 00:19:54 +01:00
Alex Orlenko c9d6a610e1 mlua-sys: v0.8.3 2025-08-10 00:11:05 +01:00
Alex Orlenko bd63f63bc9 Use ascii lowercase for module aliases
This matches with Luau 0.686 changes
2025-08-09 19:14:31 +01:00
piz-ewing c035c23a15 fix: normalize_chunk_name handles Windows paths with drive letter (#623)
Co-authored-by: ewing <ewing@MacBook-Pro.local>
2025-08-04 22:34:36 +01:00
Alex Orlenko cb153a52b2 Make Luau registered aliases case-insensitive
Executing `require("@my_module")` or `require("@My_Module")` should give the same result and use case-insensitive name.
See #620 for details
2025-07-26 22:23:16 +01:00
Alex Orlenko b1c69d3005 Use to_bits comparison to check if a float value can be represented as an integer losslessly.
This allows to simplify the code while still maintaining "negative zeros" edge case.
Thanks @JasonHise for the suggestion.
2025-07-25 21:25:08 +01:00
Alex Orlenko 841bd332e4 Fix LuaJIT negative zero tests 2025-07-25 15:24:04 +01:00
Alex Orlenko 815d1bd7c9 Better handling negative zeros to match Lua 5.3+ behavior
In Lua 5.3+ the function `lua_isinteger` returns "false" for -0.0 numbers.
In earlier Lua versions we should follow the same behavior to avoid losing the sign when converting to Integer.
Close #618
2025-07-25 14:32:47 +01:00
14 changed files with 93 additions and 21 deletions
+7
View File
@@ -1,3 +1,10 @@
## v0.11.2 (Aug 10, 2025)
- Faster stack push for `Variadic<T>`
- Fix handling Windows paths with drive letter in Luau require (#623)
- Make Luau registered aliases ascii case-insensitive (#620)
- Fix deserializing negative zeros `-0.0` (#618)
## v0.11.1 (Jul 15, 2025)
- Fixed bug exhausting Lua auxiliary stack and leaving it without reserve (#615)
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.11.1" # remember to update mlua_derive
version = "0.11.2" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
rust-version = "1.79.0"
edition = "2021"
@@ -62,7 +62,7 @@ parking_lot = { version = "0.12", features = ["arc_lock"] }
anyhow = { version = "1.0", optional = true }
rustversion = "1.0"
ffi = { package = "mlua-sys", version = "0.8.0", path = "mlua-sys" }
ffi = { package = "mlua-sys", version = "0.8.3", path = "mlua-sys" }
[dev-dependencies]
trybuild = "1.0"
@@ -78,8 +78,8 @@ tempfile = "3"
static_assertions = "1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.6", features = ["async_tokio"] }
rustyline = "16.0"
criterion = { version = "0.7", features = ["async_tokio"] }
rustyline = "17.0"
tokio = { version = "1.0", features = ["full"] }
[lints.rust]
+10 -8
View File
@@ -20,14 +20,16 @@ fn main() {
match lua.load(&line).eval::<MultiValue>() {
Ok(values) => {
editor.add_history_entry(line).unwrap();
println!(
"{}",
values
.iter()
.map(|value| format!("{:#?}", value))
.collect::<Vec<_>>()
.join("\t")
);
if values.len() > 0 {
println!(
"{}",
values
.iter()
.map(|value| format!("{:#?}", value))
.collect::<Vec<_>>()
.join("\t")
);
}
break;
}
Err(Error::SyntaxError {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua-sys"
version = "0.8.2"
version = "0.8.3"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
rust-version = "1.71"
edition = "2021"
+2 -1
View File
@@ -186,7 +186,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
// Lua 5.3+ returns "false" for `-0.0`
if n.to_bits() == (i as lua_Number).to_bits() {
return 1;
}
}
+2 -1
View File
@@ -51,7 +51,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
// Lua 5.3+ returns "false" for `-0.0`
if n.to_bits() == (i as lua_Number).to_bits() {
return 1;
}
}
+2 -1
View File
@@ -120,7 +120,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
// Lua 5.3+ returns "false" for `-0.0`
if n.to_bits() == (i as lua_Number).to_bits() {
return 1;
}
}
+21 -4
View File
@@ -129,7 +129,7 @@ impl TextRequirer {
}
fn normalize_chunk_name(chunk_name: &str) -> &str {
if let Some((path, line)) = chunk_name.split_once(':') {
if let Some((path, line)) = chunk_name.rsplit_once(':') {
if line.parse::<u32>().is_ok() {
return path;
}
@@ -567,10 +567,26 @@ pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
1
}
let (error, r#type) = unsafe {
lua.exec_raw::<(Function, Function)>((), move |state| {
unsafe extern "C-unwind" fn to_lowercase(state: *mut ffi::lua_State) -> c_int {
let s = ffi::luaL_checkstring(state, 1);
let s = CStr::from_ptr(s);
if !s.to_bytes().iter().any(|&c| c.is_ascii_uppercase()) {
// If the string does not contain any uppercase ASCII letters, return it as is
return 1;
}
callback_error_ext(state, ptr::null_mut(), true, |extra, _| {
let s = (s.to_bytes().iter())
.map(|&c| c.to_ascii_lowercase())
.collect::<bstr::BString>();
(*extra).raw_lua().push(s).map(|_| 1)
})
}
let (error, r#type, to_lowercase) = unsafe {
lua.exec_raw::<(Function, Function, Function)>((), move |state| {
ffi::lua_pushcfunctiond(state, error, cstr!("error"));
ffi::lua_pushcfunctiond(state, r#type, cstr!("type"));
ffi::lua_pushcfunctiond(state, to_lowercase, cstr!("to_lowercase"));
})
}?;
@@ -583,6 +599,7 @@ pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
env.raw_set("LOADER_CACHE", loader_cache)?;
env.raw_set("error", error)?;
env.raw_set("type", r#type)?;
env.raw_set("to_lowercase", to_lowercase)?;
lua.load(
r#"
@@ -592,7 +609,7 @@ pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
end
-- Check if the module (path) is explicitly registered
local maybe_result = REGISTERED_MODULES[path]
local maybe_result = REGISTERED_MODULES[to_lowercase(path)]
if maybe_result ~= nil then
return maybe_result
end
+9
View File
@@ -297,6 +297,15 @@ impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
MultiValue::from_lua_iter(lua, self)
}
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let nresults = self.len() as i32;
check_stack(lua.state(), nresults + 1)?;
for value in self.0 {
value.push_into_stack(lua)?;
}
Ok(nresults)
}
}
impl<T: FromLua> FromLuaMulti for Variadic<T> {
+2
View File
@@ -358,6 +358,8 @@ impl Lua {
if cfg!(feature = "luau") && !modname.starts_with('@') {
return Err(Error::runtime("module name must begin with '@'"));
}
#[cfg(feature = "luau")]
let modname = modname.to_ascii_lowercase();
unsafe {
self.exec_raw::<()>(value, |state| {
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, LOADED_MODULES_KEY);
+1 -1
View File
@@ -728,7 +728,7 @@ impl RawLua {
let n = ffi::lua_tonumber(state, idx);
match num_traits::cast(n) {
Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
Some(i) if n.to_bits() == (i as Number).to_bits() => Value::Integer(i),
_ => Value::Number(n),
}
}
+5
View File
@@ -179,6 +179,11 @@ fn test_require_with_config() {
let res = run_require(&lua, "./tests/luau/require/with_config/src/alias_requirer").unwrap();
assert_eq!("result from dependency", get_str(&res, 1));
// RequirePathWithAlias (case-insensitive)
let res2 = run_require(&lua, "./tests/luau/require/with_config/src/alias_requirer_uc").unwrap();
assert_eq!("result from dependency", get_str(&res2, 1));
assert_eq!(res.to_pointer(), res2.to_pointer());
// RequirePathWithParentAlias
let res = run_require(&lua, "./tests/luau/require/with_config/src/parent_alias_requirer").unwrap();
assert_eq!("result from other_dependency", get_str(&res, 1));
@@ -0,0 +1 @@
return require("@DeP")
+26
View File
@@ -602,6 +602,21 @@ fn test_num_conversion() -> Result<()> {
assert_eq!(lua.unpack::<i128>(lua.pack(1i128 << 64)?)?, 1i128 << 64);
// Negative zero
let negative_zero = lua.load("-0.0").eval::<f64>()?;
assert_eq!(negative_zero, 0.0);
// LuaJIT treats -0.0 as a positive zero
#[cfg(not(feature = "luajit"))]
assert!(negative_zero.is_sign_negative());
// In Lua <5.3 all numbers are floats
#[cfg(not(any(feature = "lua54", feature = "lua53", feature = "luajit")))]
{
let negative_zero = lua.load("-0").eval::<f64>()?;
assert_eq!(negative_zero, 0.0);
assert!(negative_zero.is_sign_negative());
}
Ok(())
}
@@ -1227,6 +1242,17 @@ fn test_register_module() -> Result<()> {
res.unwrap_err().to_string(),
"runtime error: module name must begin with '@'"
);
// Luau registered modules (aliases) are case-insensitive
let res = lua.register_module("@My_Module", &t);
assert!(res.is_ok());
lua.load(
r#"
local my_module = require("@MY_MODule")
assert(my_module.name == "my_module")
"#,
)
.exec()?;
}
Ok(())