use dellingr::{ArgCount, RetCount, State};
fn run_number(code: &str) -> f64 {
let mut state = State::new();
state.load_string(code).unwrap();
state
.call(ArgCount::Fixed(0), RetCount::Fixed(1))
.unwrap_or_else(|e| panic!("Error running: {code}\n{e}"));
state.to_number(-1).unwrap()
}
#[test]
fn string_method_cache_invalidates_on_string_rebind() {
let val = run_number(
r#"
local function f() return ("a"):upper() == "A" and 1 or 0 end
local first = f()
string = { upper = function(_self) return "REBOUND" end }
local second = f()
local result_after = ("a"):upper() == "REBOUND" and 1 or 0
return first * 100 + second * 10 + result_after
"#,
);
assert_eq!(val, 101.0);
}
#[test]
fn string_method_cache_survives_gc_after_string_rebind() {
let mut state = State::new();
state
.load_string(
r#"
function warm() return ("a"):upper() end
function rebind_and_drop()
string = { upper = function(_self) return "NEW" end }
end
function probe() return ("a"):upper() end
"#,
)
.unwrap();
state.call(ArgCount::Fixed(0), RetCount::Fixed(0)).unwrap();
state.get_global("warm");
state.call(ArgCount::Fixed(0), RetCount::Fixed(1)).unwrap();
state.pop(1);
state.get_global("rebind_and_drop");
state.call(ArgCount::Fixed(0), RetCount::Fixed(0)).unwrap();
state.gc_collect();
state.get_global("probe");
state.call(ArgCount::Fixed(0), RetCount::Fixed(1)).unwrap();
let result = state.to_string(-1).unwrap();
assert_eq!(result, "NEW");
}
#[test]
fn string_method_cache_blocked_by_with_restricted_env() {
let mut state = State::new();
state
.load_string(
r#"
function call_upper() return ("hi"):upper() end
"#,
)
.unwrap();
state.call(ArgCount::Fixed(0), RetCount::Fixed(0)).unwrap();
state.get_global("call_upper");
state.call(ArgCount::Fixed(0), RetCount::Fixed(1)).unwrap();
let warm = state.to_string(-1).unwrap();
assert_eq!(warm, "HI");
state.pop(1);
let restricted_result = state.with_restricted_env(&["call_upper"], |state| {
state.get_global("call_upper");
state.call(ArgCount::Fixed(0), RetCount::Fixed(1))
});
assert!(
restricted_result.is_err(),
"with_restricted_env without `string` whitelist must reject `s:upper()`, \
got: {restricted_result:?}"
);
state.get_global("call_upper");
state.call(ArgCount::Fixed(0), RetCount::Fixed(1)).unwrap();
let after = state.to_string(-1).unwrap();
assert_eq!(after, "HI");
}