use std::cell::RefCell; use std::ops::Deref; use std::sync::{Arc, Mutex}; use rlua::{Error, HookTriggers, Lua, RluaCompat, Value}; #[cfg(not(rlua_luajit))] // LuaJIT gives different results #[test] fn line_counts() { let output = Arc::new(Mutex::new(Vec::new())); let hook_output = output.clone(); let lua = Lua::new(); lua.set_hook( HookTriggers { every_line: true, ..Default::default() }, move |_lua, debug| { hook_output.lock().unwrap().push(debug.curr_line()); Ok(()) }, ); lua.context(|lua| { lua.load( r#" local x = 2 + 3 local y = x * 63 local z = string.len(x..", "..y) "#, ) .exec() .unwrap(); }); let output = output.lock().unwrap(); assert_eq!(*output, vec![2, 3, 4]); } #[test] fn function_calls() { let output = Arc::new(Mutex::new(Vec::new())); let hook_output = output.clone(); let lua = Lua::new(); lua.set_hook( HookTriggers { on_calls: true, ..Default::default() }, move |_lua, debug| { let names = debug.names(); let source = debug.source(); let name = names.name.map(|s| s.to_string()); let what = source.what; hook_output.lock().unwrap().push((name, what)); Ok(()) }, ); lua.context(|lua| { lua.load( r#" local v = string.len("Hello World") "#, ) .exec() .expect("exec error"); }); let output = output.lock().unwrap(); #[cfg(not(rlua_luajit))] assert_eq!( *output, vec![(None, "main"), (Some("len".to_string()), "C")] ); #[cfg(rlua_luajit)] assert_eq!( *output, vec![(None, "main"), (Some("len".to_string()), "Lua")] ); } #[test] fn error_within_hook() { let lua = Lua::new(); lua.set_hook( HookTriggers { every_line: true, ..Default::default() }, |_lua, _debug| { Err(Error::RuntimeError( "Something happened in there!".to_string(), )) }, ); let err = lua.context(|lua| { lua.load("x = 1") .exec() .expect_err("panic didn't propagate") }); match err { Error::CallbackError { cause, .. } => match cause.deref() { Error::RuntimeError(s) => assert_eq!(s, "Something happened in there!"), _ => panic!("wrong callback error kind caught"), }, _ => panic!("wrong error kind caught"), } } #[test] fn limit_execution_instructions() { let lua = Lua::new(); // For LuaJIT disable JIT, as compiled code does not trigger hooks #[cfg(rlua_luajit)] // LuaJIT gives different results lua.load("jit.off()").exec().unwrap(); let max_instructions = Arc::new(Mutex::new(10000)); let max_int = Arc::clone(&max_instructions); lua.set_hook( HookTriggers { every_nth_instruction: Some(30), ..Default::default() }, move |_lua, _debug| { let mut max_instructions = max_int.lock().unwrap(); (*max_instructions) -= 30; if (*max_instructions) < 0 { Err(Error::RuntimeError("time's up".to_string())) } else { Ok(()) } }, ); lua.context(|lua| { #[cfg(rlua_lua51)] lua.globals().set("x", Value::Number(0.0)).unwrap(); #[cfg(any(rlua_lua53, rlua_lua54))] lua.globals().set("x", Value::Integer(0)).unwrap(); let _ = lua .load( r#" while true do x = x + 1 end "#, ) .exec() .expect_err("instruction limit didn't occur"); }); } #[test] fn hook_removal() { let lua = Lua::new(); lua.set_hook( HookTriggers { every_nth_instruction: Some(1), ..Default::default() }, |_lua, _debug| { Err(Error::RuntimeError( "this hook should've been removed by this time".to_string(), )) }, ); lua.context(|lua| { assert!(lua.load("local x = 1").exec().is_err()); }); lua.remove_hook(); lua.context(|lua| { assert!(lua.load("local x = 1").exec().is_ok()); }); } #[test] fn hook_swap_within_hook() { thread_local! { static TL_LUA: RefCell> = RefCell::new(None); } TL_LUA.with(|tl| { *tl.borrow_mut() = Some(Lua::new()); }); TL_LUA.with(|tl| { tl.borrow().as_ref().unwrap().set_hook( HookTriggers { every_line: true, ..Default::default() }, move |lua, _debug| { lua.globals().set("ok", 1i64).unwrap(); TL_LUA.with(|tl| { tl.borrow().as_ref().unwrap().set_hook( HookTriggers { every_line: true, ..Default::default() }, move |lua, _debug| { lua.load( r#" if ok ~= nil then ok = ok + 1 end "#, ) .exec() .expect("exec failure within hook"); TL_LUA.with(|tl| { tl.borrow().as_ref().unwrap().remove_hook(); }); Ok(()) }, ); }); Ok(()) }, ); }); TL_LUA.with(|tl| { tl.borrow().as_ref().unwrap().context(|lua| { assert!(lua .load( r#" local x = 1 x = 2 local y = 3 "#, ) .exec() .is_ok()); assert_eq!(lua.globals().get::<_, i64>("ok").unwrap_or(-1), 2); }); }); }