use std::sync::Arc; use mlua::state::{GcIncParams, GcMode}; use mlua::{Error, Lua, Result, UserData}; #[cfg(any(feature = "lua54", feature = "lua55"))] use mlua::state::GcGenParams; #[test] fn test_memory_limit() -> Result<()> { let lua = Lua::new(); let initial_memory = lua.used_memory(); assert!( initial_memory > 0, "used_memory reporting is wrong, lua uses memory for stdlib" ); let f = lua .load("local t = {}; for i = 1,10000 do t[i] = i end") .into_function()?; f.call::<()>(()).expect("should trigger no memory limit"); if cfg!(feature = "luajit") && lua.set_memory_limit(0).is_err() { // seems this luajit version does not support memory limit return Ok(()); } lua.set_memory_limit(initial_memory + 10000)?; match f.call::<()>(()) { Err(Error::MemoryError(_)) => {} something_else => panic!("did not trigger memory error: {:?}", something_else), }; lua.set_memory_limit(0)?; f.call::<()>(()).expect("should trigger no memory limit"); // Test memory limit during chunk loading lua.set_memory_limit(1024)?; match lua .load("local t = {}; for i = 1,10000 do t[i] = i end") .into_function() { Err(Error::MemoryError(_)) => {} _ => panic!("did not trigger memory error"), }; Ok(()) } #[test] fn test_memory_limit_thread() -> Result<()> { let lua = Lua::new(); let f = lua .load("local t = {}; for i = 1,10000 do t[i] = i end") .into_function()?; if cfg!(feature = "luajit") && lua.set_memory_limit(0).is_err() { // seems this luajit version does not support memory limit return Ok(()); } let thread = lua.create_thread(f)?; lua.set_memory_limit(lua.used_memory() + 10000)?; match thread.resume::<()>(()) { Err(Error::MemoryError(_)) => {} something_else => panic!("did not trigger memory error: {:?}", something_else), }; Ok(()) } #[test] fn test_gc_control() -> Result<()> { let lua = Lua::new(); let globals = lua.globals(); #[cfg(any(feature = "lua55", feature = "lua54"))] { assert!(matches!( lua.gc_set_mode(GcMode::Generational(GcGenParams::default())), GcMode::Incremental(_) )); assert!(matches!( lua.gc_set_mode(GcMode::Incremental(GcIncParams::default())), GcMode::Generational(_) )); } #[cfg(any( feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau" ))] { assert!(lua.gc_is_running()); lua.gc_stop(); assert!(!lua.gc_is_running()); lua.gc_restart(); assert!(lua.gc_is_running()); } assert!(matches!( lua.gc_set_mode(GcMode::Incremental({ let p = GcIncParams::default().step_multiplier(100); #[cfg(not(feature = "luau"))] let p = p.pause(200); #[cfg(feature = "luau")] let p = p.goal(200); p })), GcMode::Incremental(_) )); struct MyUserdata(#[allow(unused)] Arc<()>); impl UserData for MyUserdata {} let rc = Arc::new(()); globals.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?; globals.raw_remove("userdata")?; assert_eq!(Arc::strong_count(&rc), 2); lua.gc_collect()?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&rc), 1); Ok(()) } #[cfg(any(feature = "lua53", feature = "lua52"))] #[test] fn test_gc_error() { use mlua::Error; let lua = Lua::new(); match lua .load( r#" val = nil table = {} setmetatable(table, { __gc = function() error("gcwascalled") end }) table = nil collectgarbage("collect") "#, ) .exec() { Err(Error::GarbageCollectorError(_)) => {} Err(e) => panic!("__gc error did not result in correct error, instead: {}", e), Ok(()) => panic!("__gc error did not result in error"), } }