Something went wrong. Try again.
Personal fork of prime agent. Mirror of: https://mnemosyne.sigilzero.dev/sigilzero/prime-agent
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676use std::collections::HashMap;#[cfg(not(target_arch = "wasm32"))]use std::iter::FromIterator;use std::panic::{catch_unwind, AssertUnwindSafe};use std::string::String as StdString;use std::sync::Arc;use std::{error, f32, f64, fmt};
use mlua::{ ffi, ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table, UserData, Value, Variadic,};
#[test]fn test_weak_lua() { let lua = Lua::new(); let weak_lua = lua.weak(); assert!(weak_lua.try_upgrade().is_some()); drop(lua); assert!(weak_lua.try_upgrade().is_none());}
#[test]#[should_panic(expected = "Lua instance is destroyed")]fn test_weak_lua_panic() { let lua = Lua::new(); let weak_lua = lua.weak(); drop(lua); let _ = weak_lua.upgrade();}
#[cfg(not(feature = "luau"))]#[test]fn test_safety() -> Result<()> { let lua = Lua::new(); assert!(lua.load(r#"require "debug""#).exec().is_err()); match lua.load_std_libs(StdLib::DEBUG) { Err(Error::SafetyError(_)) => {} Err(e) => panic!("expected SafetyError, got {:?}", e), Ok(_) => panic!("expected SafetyError, got no error"), } drop(lua);
let lua = unsafe { Lua::unsafe_new() }; assert!(lua.load(r#"require "debug""#).exec().is_ok()); drop(lua);
match Lua::new_with(StdLib::DEBUG, LuaOptions::default()) { Err(Error::SafetyError(_)) => {} Err(e) => panic!("expected SafetyError, got {:?}", e), Ok(_) => panic!("expected SafetyError, got new Lua state"), }
let lua = Lua::new(); match lua.load(r#"package.loadlib()"#).exec() { Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() { Error::SafetyError(_) => {} e => panic!("expected SafetyError cause, got {:?}", e), }, Err(e) => panic!("expected CallbackError, got {:?}", e), Ok(_) => panic!("expected CallbackError, got no error"), }; match lua.load(r#"require "fake_ffi""#).exec() { Err(Error::RuntimeError(msg)) => assert!(msg.contains("can't load C modules in safe mode")), Err(e) => panic!("expected RuntimeError, got {:?}", e), Ok(_) => panic!("expected RuntimeError, got no error"), } drop(lua);
// Test safety rules after dynamically loading `package` library let lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?; assert!(lua.globals().get::<Option<Value>>("require")?.is_none()); lua.load_std_libs(StdLib::PACKAGE)?; match lua.load(r#"package.loadlib()"#).exec() { Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() { Error::SafetyError(_) => {} e => panic!("expected SafetyError cause, got {:?}", e), }, Err(e) => panic!("expected CallbackError, got {:?}", e), Ok(_) => panic!("expected CallbackError, got no error"), };
Ok(())}
#[test]fn test_load() -> Result<()> { let lua = Lua::new();
let func = lua.load("\treturn 1+2").into_function()?; let result: i32 = func.call(())?; assert_eq!(result, 3);
assert!(lua.load("").exec().is_ok()); assert!(lua.load("§$%§&$%&").exec().is_err());
Ok(())}
#[test]fn test_exec() -> Result<()> { let lua = Lua::new();
let globals = lua.globals(); lua.load( r#" res = 'foo'..'bar' "#, ) .exec()?; assert_eq!(globals.get::<String>("res")?, "foobar");
let module: Table = lua .load( r#" local module = {}
function module.func() return "hello" end
return module "#, ) .eval()?; assert!(module.contains_key("func")?); assert_eq!(module.get::<Function>("func")?.call::<String>(())?, "hello");
Ok(())}
#[test]fn test_eval() -> Result<()> { let lua = Lua::new();
assert_eq!(lua.load("1 + 1").eval::<i32>()?, 2); assert_eq!(lua.load("false == false").eval::<bool>()?, true); assert_eq!(lua.load("return 1 + 2").eval::<i32>()?, 3); match lua.load("if true then").eval::<()>() { Err(Error::SyntaxError { incomplete_input: true, .. }) => {} r => panic!("expected SyntaxError with incomplete_input=true, got {:?}", r), }
Ok(())}
#[test]fn test_replace_globals() -> Result<()> { let lua = Lua::new();
let globals = lua.create_table()?; globals.set("foo", "bar")?;
lua.set_globals(globals.clone())?; let val = lua.load("return foo").eval::<StdString>()?; assert_eq!(val, "bar");
// Updating globals in sandboxed Lua state is not allowed #[cfg(feature = "luau")] { lua.sandbox(true)?; match lua.set_globals(globals) { Err(Error::RuntimeError(msg)) if msg.contains("cannot change globals in a sandboxed Lua state") => {} r => panic!("expected RuntimeError(...) with a specific error message, got {r:?}"), } }
Ok(())}
#[test]fn test_load_mode() -> Result<()> { let lua = unsafe { Lua::unsafe_new() };
assert_eq!(lua.load("1 + 1").set_mode(ChunkMode::Text).eval::<i32>()?, 2); match lua.load("1 + 1").set_mode(ChunkMode::Binary).exec() { Ok(_) => panic!("expected SyntaxError, got no error"), Err(Error::SyntaxError { message: msg, .. }) => { assert!(msg.contains("attempt to load a text chunk")) } Err(e) => panic!("expected SyntaxError, got {:?}", e), };
#[cfg(not(feature = "luau"))] let bytecode = lua.load("return 1 + 1").into_function()?.dump(true); #[cfg(feature = "luau")] let bytecode = mlua::Compiler::new().compile("return 1 + 1")?; assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2); assert_eq!(lua.load(&bytecode).set_mode(ChunkMode::Binary).eval::<i32>()?, 2); match lua.load(&bytecode).set_mode(ChunkMode::Text).exec() { Ok(_) => panic!("expected SyntaxError, got no error"), Err(Error::SyntaxError { message: msg, .. }) => { assert!(msg.contains("attempt to load a binary chunk")) } Err(e) => panic!("expected SyntaxError, got {:?}", e), };
Ok(())}
#[test]fn test_lua_multi() -> Result<()> { let lua = Lua::new();
lua.load( r#" function concat(arg1, arg2) return arg1 .. arg2 end
function mreturn() return 1, 2, 3, 4, 5, 6 end "#, ) .exec()?;
let globals = lua.globals(); let concat = globals.get::<Function>("concat")?; let mreturn = globals.get::<Function>("mreturn")?;
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar"); let (a, b) = mreturn.call::<(u64, u64)>(())?; assert_eq!((a, b), (1, 2)); let (a, b, v) = mreturn.call::<(u64, u64, Variadic<u64>)>(())?; assert_eq!((a, b), (1, 2)); assert_eq!(v[..], [3, 4, 5, 6]);
Ok(())}
#[test]fn test_coercion() -> Result<()> { let lua = Lua::new();
lua.load( r#" int = 123 str = "123" num = 123.0 func = function() end "#, ) .exec()?;
let globals = lua.globals(); assert_eq!(globals.get::<String>("int")?, "123"); assert_eq!(globals.get::<i32>("str")?, 123); assert_eq!(globals.get::<i32>("num")?, 123); assert!(globals.get::<String>("func").is_err());
Ok(())}
#[test]fn test_error() -> Result<()> { #[derive(Debug)] pub struct TestError;
impl fmt::Display for TestError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "test error") } }
impl error::Error for TestError {}
let lua = Lua::new();
let globals = lua.globals(); lua.load( r#" function no_error() end
function lua_error() error("this is a lua error") end
function rust_error() rust_error_function() end
function return_error() local status, res = pcall(rust_error_function) assert(not status) return res end
function return_string_error() return "this should be converted to an error" end
function test_pcall() local testvar = 0
pcall(function(arg) testvar = testvar + arg error("should be ignored") end, 3)
local function handler(err) if string.match(_VERSION, " 5%.1$") or string.match(_VERSION, " 5%.2$") or string.match(_VERSION, "Luau") then -- Special case for Lua 5.1/5.2 and Luau local caps = string.match(err, ': (%d+)$') if caps then err = caps end end testvar = testvar + err return "should be ignored" end
local status, res = xpcall(function() error(5) end, handler) assert(not status)
if testvar ~= 8 then error("testvar had the wrong value, pcall / xpcall misbehaving "..testvar) end end
function understand_recursion() understand_recursion() end "#, ) .exec()?;
let rust_error_function = lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?; globals.set("rust_error_function", rust_error_function)?;
let no_error = globals.get::<Function>("no_error")?; assert!(no_error.call::<()>(()).is_ok());
let lua_error = globals.get::<Function>("lua_error")?; match lua_error.call::<()>(()) { Err(Error::RuntimeError(_)) => {} Err(e) => panic!("error is not RuntimeError kind, got {:?}", e), _ => panic!("error not returned"), }
let rust_error = globals.get::<Function>("rust_error")?; match rust_error.call::<()>(()) { Err(Error::CallbackError { .. }) => {} Err(e) => panic!("error is not CallbackError kind, got {:?}", e), _ => panic!("error not returned"), }
let return_error = globals.get::<Function>("return_error")?; match return_error.call::<Value>(()) { Ok(Value::Error(_)) => {} _ => panic!("Value::Error not returned"), }
let return_string_error = globals.get::<Function>("return_string_error")?; assert!(return_string_error.call::<Error>(()).is_ok());
match lua.load("if you are happy and you know it syntax error").exec() { Err(Error::SyntaxError { incomplete_input: false, .. }) => {} Err(_) => panic!("error is not LuaSyntaxError::Syntax kind"), _ => panic!("error not returned"), } match lua.load("function i_will_finish_what_i()").exec() { Err(Error::SyntaxError { incomplete_input: true, .. }) => {} Err(_) => panic!("error is not LuaSyntaxError::IncompleteStatement kind"), _ => panic!("error not returned"), }
let test_pcall = globals.get::<Function>("test_pcall")?; test_pcall.call::<()>(())?;
#[cfg(not(target_arch = "wasm32"))] { let understand_recursion = globals.get::<Function>("understand_recursion")?; assert!(understand_recursion.call::<()>(()).is_err()); }
Ok(())}
#[test]#[cfg(not(panic = "abort"))]fn test_panic() -> Result<()> { fn make_lua(options: LuaOptions) -> Result<Lua> { let lua = Lua::new_with(StdLib::ALL_SAFE, options)?; let rust_panic_function = lua.create_function(|_, msg: Option<StdString>| -> Result<()> { if let Some(msg) = msg { panic!("{}", msg) } panic!("rust panic") })?; lua.globals().set("rust_panic_function", rust_panic_function)?; Ok(lua) }
// Test triggering Lua error with sending Rust panic (must be resumed) { let lua = make_lua(LuaOptions::default())?;
match catch_unwind(AssertUnwindSafe(|| -> Result<()> { lua.load( r#" _, err = pcall(rust_panic_function) error(err) "#, ) .exec() })) { Ok(Ok(_)) => panic!("no panic was detected"), Ok(Err(e)) => panic!("error during panic test {:?}", e), Err(p) => assert!(*p.downcast::<&str>().unwrap() == "rust panic"), };
// Trigger same panic again match lua.load("error(err)").exec() { Ok(_) => panic!("no error was detected"), Err(Error::PreviouslyResumedPanic) => {} Err(e) => panic!("expected PreviouslyResumedPanic, got {:?}", e), } }
// Test returning Rust panic (must be resumed) { let lua = make_lua(LuaOptions::default())?; match catch_unwind(AssertUnwindSafe(|| -> Result<()> { let _caught_panic = lua .load( r#" -- Set global _, err = pcall(rust_panic_function) return err "#, ) .eval::<Value>()?; Ok(()) })) { Ok(_) => panic!("no panic was detected"), Err(_) => {} };
assert!(lua.globals().get::<Value>("err")? == Value::Nil); match lua.load("tostring(err)").exec() { Ok(_) => panic!("no error was detected"), Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() { Error::PreviouslyResumedPanic => {} e => panic!("expected PreviouslyResumedPanic, got {:?}", e), }, Err(e) => panic!("expected CallbackError, got {:?}", e), } }
// Test representing Rust panic as a string match catch_unwind(|| -> Result<()> { let lua = make_lua(LuaOptions::default())?; lua.load( r#" local _, err = pcall(rust_panic_function) error(tostring(err)) "#, ) .exec() }) { Ok(Ok(_)) => panic!("no error was detected"), Ok(Err(Error::RuntimeError(_))) => {} Ok(Err(e)) => panic!("expected RuntimeError, got {:?}", e), Err(_) => panic!("panic was detected"), }
// Test disabling `catch_rust_panics` option / pcall correctness match catch_unwind(|| -> Result<()> { let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?; lua.load( r#" local ok, err = pcall(function(msg) error(msg) end, "hello") assert(not ok and err:find("hello") ~= nil)
ok, err = pcall(rust_panic_function, "rust panic from lua") -- Nothing to return, panic should be automatically resumed "#, ) .exec() }) { Ok(r) => panic!("no panic was detected: {:?}", r), Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"), }
// Test disabling `catch_rust_panics` option / xpcall correctness match catch_unwind(|| -> Result<()> { let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?; lua.load( r#" local msgh_ok = false local msgh = function(err) msgh_ok = err ~= nil and err:find("hello") ~= nil return err end local ok, err = xpcall(function(msg) error(msg) end, msgh, "hello") assert(not ok and err:find("hello") ~= nil) assert(msgh_ok)
ok, err = xpcall(rust_panic_function, msgh, "rust panic from lua") -- Nothing to return, panic should be automatically resumed "#, ) .exec() }) { Ok(r) => panic!("no panic was detected: {:?}", r), Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"), }
Ok(())}
#[cfg(target_pointer_width = "64")]#[test]fn test_safe_integers() -> Result<()> { const MAX_SAFE_INTEGER: i64 = 2i64.pow(53) - 1; const MIN_SAFE_INTEGER: i64 = -2i64.pow(53) + 1;
let lua = Lua::new(); let f = lua.load("return ...").into_function()?;
assert_eq!(f.call::<i64>(MAX_SAFE_INTEGER)?, MAX_SAFE_INTEGER); assert_eq!(f.call::<i64>(MIN_SAFE_INTEGER)?, MIN_SAFE_INTEGER);
// For Lua versions that does not support 64-bit integers, the values will be converted to f64 #[cfg(any(feature = "luau", feature = "lua51", feature = "luajit"))] { assert_ne!(f.call::<i64>(MAX_SAFE_INTEGER + 2)?, MAX_SAFE_INTEGER + 2); assert_ne!(f.call::<i64>(MIN_SAFE_INTEGER - 2)?, MIN_SAFE_INTEGER - 2); assert_eq!(f.call::<f64>(i64::MAX)?, i64::MAX as f64); }
Ok(())}
#[test]fn test_num_conversion() -> Result<()> { let lua = Lua::new();
assert_eq!( lua.coerce_integer(Value::String(lua.create_string("1")?))?, Some(1) ); assert_eq!( lua.coerce_integer(Value::String(lua.create_string("1.0")?))?, Some(1) ); assert_eq!( lua.coerce_integer(Value::String(lua.create_string("1.5")?))?, None );
assert_eq!( lua.coerce_number(Value::String(lua.create_string("1")?))?, Some(1.0) ); assert_eq!( lua.coerce_number(Value::String(lua.create_string("1.0")?))?, Some(1.0) ); assert_eq!( lua.coerce_number(Value::String(lua.create_string("1.5")?))?, Some(1.5) );
assert_eq!(lua.load("1.0").eval::<i64>()?, 1); assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0); #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] assert_eq!(lua.load("1.0").eval::<String>()?, "1.0"); #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))] assert_eq!(lua.load("1.0").eval::<String>()?, "1");
assert_eq!(lua.load("1.5").eval::<i64>()?, 1); assert_eq!(lua.load("1.5").eval::<f64>()?, 1.5); assert_eq!(lua.load("1.5").eval::<String>()?, "1.5");
assert!(lua.load("-1").eval::<u64>().is_err()); assert_eq!(lua.load("-1").eval::<i64>()?, -1);
assert!(lua.unpack::<u64>(lua.pack(1u128 << 64)?).is_err()); assert!(lua.load("math.huge").eval::<i64>().is_err());
assert_eq!(lua.unpack::<f64>(lua.pack(f32::MAX)?)?, f32::MAX as f64); assert_eq!(lua.unpack::<f64>(lua.pack(f32::MIN)?)?, f32::MIN as f64); assert_eq!(lua.unpack::<f32>(lua.pack(f64::MAX)?)?, f32::INFINITY); assert_eq!(lua.unpack::<f32>(lua.pack(f64::MIN)?)?, f32::NEG_INFINITY);
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 = "lua55", 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(())}
#[test]fn test_pcall_xpcall() -> Result<()> { let lua = Lua::new(); let globals = lua.globals();
// make sure that we handle not enough arguments
assert!(lua.load("pcall()").exec().is_err()); assert!(lua.load("xpcall()").exec().is_err()); assert!(lua.load("xpcall(function() end)").exec().is_err());
// Lua >= 5.2 compatible version of xpcall for 5.1 #[cfg(feature = "lua51")] lua.load( r#" local xpcall_orig = xpcall function xpcall(f, err, ...) return xpcall_orig(function() return f(unpack(arg)) end, err) end "#, ) .exec()?;
// Make sure that the return values from are correct on success
let (r, e) = lua .load("pcall(function(p) return p end, 'foo')") .eval::<(bool, String)>()?; assert!(r); assert_eq!(e, "foo");
let (r, e) = lua .load("xpcall(function(p) return p end, print, 'foo')") .eval::<(bool, String)>()?; assert!(r); assert_eq!(e, "foo");
// Make sure that the return values are correct on errors, and that error handling works
lua.load( r#" pcall_error = nil pcall_status, pcall_error = pcall(error, "testerror")
xpcall_error = nil xpcall_status, _ = xpcall(error, function(err) xpcall_error = err end, "testerror") "#, ) .exec()?;
assert_eq!(globals.get::<bool>("pcall_status")?, false); assert_eq!(globals.get::<String>("pcall_error")?, "testerror");
assert_eq!(globals.get::<bool>("xpcall_statusr")?, false); #[cfg(any( feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit" ))] assert_eq!(globals.get::<std::string::String>("xpcall_error")?, "testerror"); #[cfg(feature = "lua51")] assert!(globals .get::<String>("xpcall_error")? .to_str()? .ends_with(": testerror"));
// Make sure that weird xpcall error recursion at least doesn't cause unsafety or panics. lua.load( r#" function xpcall_recursion() xpcall(error, function(err) error(err) end, "testerror") end "#, ) .exec()?; let _ = globals.get::<Function>("xpcall_recursion")?.call::<()>(());
Ok(())}
#[test]fn test_recursive_mut_callback_error() -> Result<()> { let lua = Lua::new();
let mut v = Some(Box::new(123)); let f = lua.create_function_mut(move |lua, mutate: bool| { if mutate { v = None; } else { // Produce a mutable reference let r = v.as_mut().unwrap(); // Whoops, this will recurse into the function and produce another mutable reference! lua.globals().get::<Function>("f")?.call::<()>(true)?; println!("Should not get here, mutable aliasing has occurred!"); println!("value at {:p} is {r}", r as *mut _); }
Ok(()) })?; lua.globals().set("f", f)?; match lua.globals().get::<Function>("f")?.call::<()>(false) { Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() { Error::CallbackError { ref cause, .. } => match *cause.as_ref() { Error::RecursiveMutCallback { .. } => {} ref other => panic!("incorrect result: {:?}", other), }, ref other => panic!("incorrect result: {:?}", other), }, other => panic!("incorrect result: {:?}", other), };
Ok(())}
#[test]fn test_set_metatable_nil() -> Result<()> { let lua = Lua::new(); lua.load( r#" a = {} setmetatable(a, nil) "#, ) .exec()?; Ok(())}
#[test]fn test_named_registry_value() -> Result<()> { let lua = Lua::new();
lua.set_named_registry_value("test", 42)?; let f = lua.create_function(move |lua, ()| { assert_eq!(lua.named_registry_value::<i32>("test")?, 42); Ok(()) })?;
f.call::<()>(())?;
lua.unset_named_registry_value("test")?; match lua.named_registry_value("test")? { Nil => {} val => panic!("registry value was not Nil, was {:?}", val), };
Ok(())}
#[test]fn test_registry_value() -> Result<()> { let lua = Lua::new();
let mut r = Some(lua.create_registry_value(42)?); let f = lua.create_function_mut(move |lua, ()| { if let Some(r) = r.take() { assert_eq!(lua.registry_value::<i32>(&r)?, 42); lua.remove_registry_value(r).unwrap(); } else { panic!(); } Ok(()) })?;
f.call::<()>(())?;
Ok(())}
#[test]fn test_drop_registry_value() -> Result<()> { struct MyUserdata(#[allow(unused)] Arc<()>);
impl UserData for MyUserdata {}
let lua = Lua::new(); let rc = Arc::new(());
let r = lua.create_registry_value(MyUserdata(rc.clone()))?; assert_eq!(Arc::strong_count(&rc), 2);
drop(r); lua.expire_registry_values();
lua.load(r#"collectgarbage("collect")"#).exec()?;
assert_eq!(Arc::strong_count(&rc), 1);
Ok(())}
#[test]fn test_replace_registry_value() -> Result<()> { let lua = Lua::new();
let mut key = lua.create_registry_value(42)?; lua.replace_registry_value(&mut key, "new value")?; assert_eq!(lua.registry_value::<String>(&key)?, "new value"); lua.replace_registry_value(&mut key, Value::Nil)?; assert_eq!(lua.registry_value::<Value>(&key)?, Value::Nil); lua.replace_registry_value(&mut key, 123)?; assert_eq!(lua.registry_value::<i32>(&key)?, 123);
let mut key2 = lua.create_registry_value(Value::Nil)?; lua.replace_registry_value(&mut key2, Value::Nil)?; assert_eq!(lua.registry_value::<Value>(&key2)?, Value::Nil); lua.replace_registry_value(&mut key2, "abc")?; assert_eq!(lua.registry_value::<String>(&key2)?, "abc");
Ok(())}
#[test]fn test_lua_registry_hash() -> Result<()> { let lua = Lua::new();
let r1 = Arc::new(lua.create_registry_value("value1")?); let r2 = Arc::new(lua.create_registry_value("value2")?);
let mut map = HashMap::new(); map.insert(r1.clone(), "value1"); map.insert(r2.clone(), "value2");
assert_eq!(map[&r1], "value1"); assert_eq!(map[&r2], "value2");
Ok(())}
#[test]fn test_lua_registry_ownership() -> Result<()> { let lua1 = Lua::new(); let lua2 = Lua::new();
let r1 = lua1.create_registry_value("hello")?; let r2 = lua2.create_registry_value("hello")?;
assert!(lua1.owns_registry_value(&r1)); assert!(!lua2.owns_registry_value(&r1)); assert!(lua2.owns_registry_value(&r2)); assert!(!lua1.owns_registry_value(&r2));
Ok(())}
#[test]fn test_mismatched_registry_key() -> Result<()> { let lua1 = Lua::new(); let lua2 = Lua::new();
let r = lua1.create_registry_value("hello")?; match lua2.remove_registry_value(r) { Err(Error::MismatchedRegistryKey) => {} r => panic!("wrong result type for mismatched registry key, {:?}", r), };
Ok(())}
#[test]fn test_registry_value_reuse() -> Result<()> { let lua = Lua::new();
let r1 = lua.create_registry_value("value1")?; let r1_slot = format!("{r1:?}"); drop(r1);
// Previous slot must not be reused by nil value let r2 = lua.create_registry_value(Value::Nil)?; let r2_slot = format!("{r2:?}"); assert_ne!(r1_slot, r2_slot); drop(r2);
// But should be reused by non-nil value let r3 = lua.create_registry_value("value3")?; let r3_slot = format!("{r3:?}"); assert_eq!(r1_slot, r3_slot);
Ok(())}
#[test]#[cfg(not(panic = "abort"))]fn test_application_data() -> Result<()> { let lua = Lua::new();
lua.set_app_data("test1"); lua.set_app_data(vec!["test2"]);
// Borrow &str immutably and Vec<&str> mutably let s = lua.app_data_ref::<&str>().unwrap(); let mut v = lua.app_data_mut::<Vec<&str>>().unwrap(); v.push("test3");
// Insert of new data or removal should fail now assert!(lua.try_set_app_data::<i32>(123).is_err()); match catch_unwind(AssertUnwindSafe(|| lua.set_app_data::<i32>(123))) { Ok(_) => panic!("expected panic"), Err(_) => {} } match catch_unwind(AssertUnwindSafe(|| lua.remove_app_data::<i32>())) { Ok(_) => panic!("expected panic"), Err(_) => {} }
// Check display and debug impls assert_eq!(format!("{s}"), "test1"); assert_eq!(format!("{s:?}"), "\"test1\"");
// Borrowing immutably and mutably of the same type is not allowed assert!(lua.try_app_data_mut::<&str>().is_err()); match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) { Ok(_) => panic!("expected panic"), Err(_) => {} } assert!(lua.try_app_data_ref::<Vec<&str>>().is_err()); drop((s, v));
// Test that application data is accessible from anywhere let f = lua.create_function(|lua, ()| { let mut data1 = lua.app_data_mut::<&str>().unwrap(); assert_eq!(*data1, "test1"); *data1 = "test4";
let data2 = lua.app_data_ref::<Vec<&str>>().unwrap(); assert_eq!(*data2, vec!["test2", "test3"]);
Ok(()) })?; f.call::<()>(())?;
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4"); assert_eq!(*lua.app_data_ref::<Vec<&str>>().unwrap(), vec!["test2", "test3"]);
lua.remove_app_data::<Vec<&str>>(); assert!(matches!(lua.app_data_ref::<Vec<&str>>(), None));
Ok(())}
#[test]fn test_rust_function() -> Result<()> { let lua = Lua::new();
let globals = lua.globals(); lua.load( r#" function lua_function() return rust_function() end
-- Test to make sure chunk return is ignored return 1 "#, ) .exec()?;
let lua_function = globals.get::<Function>("lua_function")?; let rust_function = lua.create_function(|_, ()| Ok("hello"))?;
globals.set("rust_function", rust_function)?; assert_eq!(lua_function.call::<String>(())?, "hello");
Ok(())}
#[test]fn test_c_function() -> Result<()> { let lua = Lua::new();
extern "C-unwind" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int { unsafe { ffi::lua_pushboolean(state, 1); ffi::lua_setglobal(state, b"c_function\0" as *const _ as *const _); } 0 }
let func = unsafe { lua.create_c_function(c_function)? }; func.call::<()>(())?; assert_eq!(lua.globals().get::<bool>("c_function")?, true);
Ok(())}
#[test]#[cfg(not(target_arch = "wasm32"))]fn test_recursion() -> Result<()> { let lua = Lua::new();
let f = lua.create_function(move |lua, i: i32| { if i < 64 { lua.globals().get::<Function>("f")?.call::<()>(i + 1)?; } Ok(()) })?;
lua.globals().set("f", &f)?; f.call::<()>(1)?;
Ok(())}
#[test]#[cfg(not(target_arch = "wasm32"))]fn test_too_many_returns() -> Result<()> { let lua = Lua::new(); let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?; assert!(f.call::<Variadic<u32>>(()).is_err()); Ok(())}
#[test]#[cfg(not(target_arch = "wasm32"))]fn test_too_many_arguments() -> Result<()> { let lua = Lua::new(); lua.load("function test(...) end").exec()?; let args = Variadic::from_iter(1..1000000); assert!(lua.globals().get::<Function>("test")?.call::<()>(args).is_err()); Ok(())}
#[test]#[cfg(not(feature = "luajit"))]#[cfg(not(target_arch = "wasm32"))]fn test_too_many_recursions() -> Result<()> { let lua = Lua::new();
let f = lua.create_function(move |lua, ()| lua.globals().get::<Function>("f")?.call::<()>(()))?;
lua.globals().set("f", &f)?; assert!(f.call::<()>(()).is_err());
Ok(())}
#[test]#[cfg(not(target_arch = "wasm32"))]fn test_ref_stack_exhaustion() { match catch_unwind(AssertUnwindSafe(|| -> Result<()> { let lua = Lua::new(); let mut vals = Vec::new(); for _ in 0..10000000 { vals.push(lua.create_table()?); } Ok(()) })) { Ok(_) => panic!("no panic was detected"), Err(p) => assert!(p .downcast::<StdString>() .unwrap() .starts_with("cannot create a Lua reference, out of auxiliary stack space")), }}
#[test]fn test_large_args() -> Result<()> { let lua = Lua::new(); let globals = lua.globals();
globals.set( "c", lua.create_function(|_, args: Variadic<usize>| { let mut s = 0; for i in 0..args.len() { s += i; assert_eq!(i, args[i]); } Ok(s) })?, )?;
let f: Function = lua .load( r#" return function(...) return c(...) end "#, ) .eval()?;
assert_eq!(f.call::<usize>((0..100).collect::<Variadic<usize>>())?, 4950);
Ok(())}
#[test]fn test_large_args_ref() -> Result<()> { let lua = Lua::new();
let f = lua.create_function(|_, args: Variadic<String>| { for i in 0..args.len() { assert_eq!(args[i], i.to_string()); } Ok(()) })?;
f.call::<()>((0..100).map(|i| i.to_string()).collect::<Variadic<_>>())?;
Ok(())}
#[test]fn test_chunk_env() -> Result<()> { let lua = Lua::new();
let assert: Function = lua.globals().get("assert")?;
let env1 = lua.create_table()?; env1.set("assert", assert.clone())?;
let env2 = lua.create_table()?; env2.set("assert", assert)?;
lua.load( r#" test_var = 1 "#, ) .set_environment(env1.clone()) .exec()?;
lua.load( r#" assert(test_var == nil) test_var = 2 "#, ) .set_environment(env2.clone()) .exec()?;
assert_eq!(lua.load("test_var").set_environment(env1).eval::<i32>()?, 1); assert_eq!(lua.load("test_var").set_environment(env2).eval::<i32>()?, 2);
Ok(())}
#[test]fn test_context_thread() -> Result<()> { let lua = Lua::new();
let f = lua .load( r#" local thread = ... assert(coroutine.running() == thread) "#, ) .into_function()?;
#[cfg(any( feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52" ))] f.call::<()>(lua.current_thread())?;
#[cfg(any( feature = "lua51", all(feature = "luajit", not(feature = "luajit52")), feature = "luau" ))] f.call::<()>(Nil)?;
Ok(())}
#[test]#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "luajit52"))))]fn test_context_thread_51() -> Result<()> { let lua = Lua::new();
let thread = lua.create_thread( lua.load( r#" function (thread) assert(coroutine.running() == thread) end "#, ) .eval()?, )?;
thread.resume::<()>(thread.clone())?;
Ok(())}
#[test]#[cfg(feature = "luajit")]fn test_jit_version() -> Result<()> { let lua = Lua::new(); let jit: Table = lua.globals().get("jit")?; assert!(jit.get::<String>("version")?.to_str()?.contains("LuaJIT")); Ok(())}
#[test]fn test_register_module() -> Result<()> { let lua = Lua::new();
let t = lua.create_table()?; t.set("name", "my_module")?; lua.register_module("@my_module", &t)?;
lua.load( r#" local my_module = require("@my_module") assert(my_module.name == "my_module") "#, ) .exec()?;
lua.unload_module("@my_module")?; lua.load( r#" local ok, err = pcall(function() return require("@my_module") end) assert(not ok) "#, ) .exec()?;
#[cfg(feature = "luau")] { // Luau registered modules must have '@' prefix let res = lua.register_module("my_module", 123); assert!(res.is_err()); assert_eq!( 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(())}
#[test]#[cfg(not(feature = "luau"))]fn test_preload_module() -> Result<()> { let lua = Lua::new();
let loader = lua.create_function(move |lua, modname: String| { let t = lua.create_table()?; t.set("name", modname)?; Ok(t) })?;
lua.preload_module("@my_module", loader.clone())?; lua.load( r#" -- `my_module` is global for purposes of next test my_module = require("@my_module") assert(my_module.name == "@my_module") local my_module2 = require("@my_module") assert(my_module == my_module2) "#, ) .exec() .unwrap();
// Test unloading and loading again lua.unload_module("@my_module")?; lua.load( r#" local my_module3 = require("@my_module") -- `my_module` is not equal to `my_module3` because it was reloaded assert(my_module ~= my_module3) "#, ) .exec() .unwrap();
Ok(())}
#[test]fn test_inspect_stack() -> Result<()> { let lua = Lua::new();
// Not inside any function assert!(lua.inspect_stack(0, |_| ()).is_none());
let logline = lua.create_function(|lua, msg: StdString| { let r = lua .inspect_stack(1, |debug| { let source = debug.source().short_src; let source = source.as_deref().unwrap_or("?"); let line = debug.current_line().unwrap(); format!("{}:{} {}", source, line, msg) }) .unwrap(); Ok(r) })?; lua.globals().set("logline", logline)?;
lua.load( r#" local function foo() local line = logline("hello") return line end local function bar() return foo() end
assert(foo() == '[string "chunk"]:3 hello') assert(bar() == '[string "chunk"]:3 hello') assert(logline("world") == '[string "chunk"]:12 world') "#, ) .set_name("chunk") .exec()?;
let stack_info = lua.create_function(|lua, ()| { let stack_info = lua.inspect_stack(1, |debug| debug.stack()).unwrap(); Ok(format!("{stack_info:?}")) })?; lua.globals().set("stack_info", stack_info)?;
#[cfg(any( feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau" ))] lua.load( r#" local stack_info = stack_info local function baz(a, b, c, ...) return stack_info() end assert(baz() == 'DebugStack { num_ups: 1, num_params: 3, is_vararg: true }') "#, ) .exec()?;
// LuaJIT does not pass this test for some reason #[cfg(feature = "lua51")] lua.load( r#" local stack_info = stack_info local function baz(a, b, c, ...) return stack_info() end assert(baz() == 'DebugStack { num_ups: 1 }') "#, ) .exec()?;
// Test retrieving currently running function let running_function = lua.create_function(|lua, ()| Ok(lua.inspect_stack(1, |debug| debug.function())))?; lua.globals().set("running_function", running_function)?; lua.load( r#" local function baz() return running_function() end if jit == nil then assert(baz() == baz) else -- luajit inline the "baz" function and returns the chunk itself assert(baz() == running_function()) end "#, ) .exec()?;
Ok(())}
#[test]fn test_traceback() -> Result<()> { let lua = Lua::new();
// Test traceback at level 0 (not inside any function) let traceback = lua.traceback(None, 0)?.to_string_lossy(); assert!(traceback.contains("stack traceback:"));
// Test traceback with a message prefix let traceback = lua.traceback(Some("error occurred"), 0)?.to_string_lossy(); assert!(traceback.starts_with("error occurred")); assert!(traceback.contains("stack traceback:"));
// Test traceback inside a function let get_traceback = lua.create_function(|lua, (msg, level): (Option<StdString>, usize)| { lua.traceback(msg.as_deref(), level) })?; lua.globals().set("get_traceback", get_traceback)?;
lua.load( r#" local function foo() -- Level 1 is inside foo (the caller) local traceback = get_traceback(nil, 1) return traceback end local function bar() local result = foo() return result end local function baz() local result = bar() return result end
local traceback = baz() assert(traceback:match("in %a+ 'foo'")) assert(traceback:match("in %a+ 'bar'")) assert(traceback:match("in %a+ 'baz'")) "#, ) .exec()?;
// Test traceback at different levels lua.load( r#" local function foo() local tb0 = get_traceback(nil, 0) local tb1 = get_traceback(nil, 1) local tb2 = get_traceback(nil, 2) return tb0, tb1, tb2 end local function bar() local tb0, tb1, tb2 = foo() return tb0, tb1, tb2 end
local tb0, tb1, tb2 = bar()
assert(tb0:match("in %a+ 'get_traceback'")) assert(tb0:match("in %a+ 'foo'"))
assert(not tb1:match("in %a+ 'get_traceback'")) assert(tb1:match("in %a+ 'foo'"))
assert(not tb2:match("in %a+ 'foo'")) assert(tb1:match("in %a+ 'bar'")) "#, ) .exec()?;
Ok(())}
#[test]fn test_multi_states() -> Result<()> { let lua = Lua::new();
let f = lua.create_function(|_, g: Option<Function>| { if let Some(g) = g { g.call::<()>(())?; } Ok(()) })?; lua.globals().set("f", f)?;
lua.load("f(function() coroutine.wrap(function() f() end)() end)") .exec()?;
Ok(())}
#[test]#[cfg(any(feature = "lua55", feature = "lua54"))]fn test_warnings() -> Result<()> { let lua = Lua::new(); lua.set_app_data::<Vec<(StdString, bool)>>(Vec::new());
lua.set_warning_function(|lua, msg, incomplete| { lua.app_data_mut::<Vec<(StdString, bool)>>() .unwrap() .push((msg.to_string(), incomplete)); Ok(()) });
lua.warning("native warning ...", true); lua.warning("finish", false); lua.warning("\0", false); lua.load(r#"warn("lua warning", "continue")"#).exec()?;
lua.remove_warning_function(); lua.warning("one more warning", false);
let messages = lua.app_data_ref::<Vec<(StdString, bool)>>().unwrap(); assert_eq!( *messages, vec![ ("native warning ...".to_string(), true), ("finish".to_string(), false), ("".to_string(), false), ("lua warning".to_string(), true), ("continue".to_string(), false), ] );
// Trigger error inside warning lua.set_warning_function(|_, _, _| Err(Error::runtime("warning error"))); assert!(matches!( lua.load(r#"warn("test")"#).exec(), Err(Error::RuntimeError(ref err)) if err == "warning error" ));
// Recursive warning lua.set_warning_function(|lua, _, _| { lua.warning("inner", false); Ok(()) }); lua.warning("hello", false);
Ok(())}
#[test]#[cfg(feature = "luajit")]fn test_luajit_cdata() -> Result<()> { let lua = unsafe { Lua::unsafe_new() };
let cdata = 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::<Value>()?; assert_eq!(cdata.type_name(), "other"); assert!(cdata.to_string()?.starts_with("cdata<void *>:"));
Ok(())}
#[test]#[cfg(feature = "send")]#[cfg(not(target_arch = "wasm32"))]fn test_multi_thread() -> Result<()> { let lua = Lua::new();
lua.globals().set("i", 0)?; let func = lua.load("i = i + 1").into_function()?;
std::thread::scope(|s| { s.spawn(|| { for _ in 0..5 { func.call::<()>(()).unwrap(); } }); s.spawn(|| { for _ in 0..5 { func.call::<()>(()).unwrap(); } }); });
assert_eq!(lua.globals().get::<i32>("i")?, 10);
Ok(())}
#[test]fn test_exec_raw() -> Result<()> { let lua = Lua::new();
let sum = lua.create_function(|_, args: Variadic<i32>| { let mut sum = 0; for i in args { sum += i; } Ok(sum) })?; lua.globals().set("sum", sum)?;
let n: i32 = unsafe { lua.exec_raw((), |state| { ffi::lua_getglobal(state, b"sum\0".as_ptr() as _); ffi::lua_pushinteger(state, 1); ffi::lua_pushinteger(state, 7); ffi::lua_call(state, 2, 1); }) }?; assert_eq!(n, 8);
// Test error handling let res: Result<()> = unsafe { lua.exec_raw("test error", |state| { ffi::lua_error(state); }) }; assert!(matches!(res, Err(Error::RuntimeError(err)) if err.contains("test error")));
Ok(())}
#[test]fn test_gc_drop_ref_thread() -> Result<()> { let lua = Lua::new();
let t = lua.create_table()?; lua.create_function(move |_, ()| { _ = &t; Ok(()) })?;
for _ in 0..10000 { // GC will run eventually to collect the function and the table above lua.create_table()?; }
Ok(())}
#[cfg(not(feature = "luau"))]#[test]fn test_get_or_init_from_ptr() -> Result<()> { // This would not work with Luau, the state must be init by mlua internally let state = unsafe { ffi::luaL_newstate() };
let mut lua = unsafe { Lua::get_or_init_from_ptr(state) }; lua.globals().set("hello", "world678")?;
// The same Lua instance must be returned lua = unsafe { Lua::get_or_init_from_ptr(state) }; assert_eq!(lua.globals().get::<String>("hello")?, "world678");
unsafe { ffi::lua_close(state) };
// Lua must not be accessed after closing
Ok(())}