Skip to main content

luaur_repl_cli/functions/
safe_get_table.rs

1use core::ffi::c_char;
2
3use luaur_vm::functions::lua_l_getmetafield::lua_l_getmetafield;
4use luaur_vm::functions::lua_pushnil::lua_pushnil;
5use luaur_vm::functions::lua_pushvalue::lua_pushvalue;
6use luaur_vm::functions::lua_rawget::lua_rawget;
7use luaur_vm::functions::lua_remove::lua_remove;
8use luaur_vm::functions::lua_replace::lua_replace;
9use luaur_vm::macros::lua_isnil::lua_isnil;
10use luaur_vm::macros::lua_istable::lua_istable;
11use luaur_vm::macros::lua_pop::lua_pop;
12use luaur_vm::type_aliases::lua_state::lua_State;
13
14// Mirrors MaxTraversalLimit in Repl.cpp.
15const MAX_TRAVERSAL_LIMIT: i32 = 50;
16
17// This function is similar to lua_gettable, but it avoids calling any
18// lua callback functions (e.g. __index) which might modify the Lua VM state.
19pub unsafe fn safe_get_table(l: *mut lua_State, table_index: i32) {
20    lua_pushvalue(l, table_index); // Duplicate the table
21
22    // The loop invariant is that the table to search is at -1
23    // and the key is at -2.
24    let mut loop_count = 0;
25    loop {
26        lua_pushvalue(l, -2); // Duplicate the key
27        lua_rawget(l, -2); // Try to find the key
28        if !lua_isnil!(l, -1) || loop_count >= MAX_TRAVERSAL_LIMIT {
29            // Either the key has been found, and/or we have reached the max traversal limit
30            break;
31        } else {
32            lua_pop(l, 1); // Pop the nil result
33            if lua_l_getmetafield(l, -1, c"__index".as_ptr() as *const c_char) == 0 {
34                lua_pushnil(l);
35                break;
36            } else if lua_istable!(l, -1) {
37                // Replace the current table being searched with __index table
38                lua_replace(l, -2);
39            } else {
40                lua_pop(l, 1); // Pop the value
41                lua_pushnil(l);
42                break;
43            }
44        }
45
46        loop_count += 1;
47    }
48
49    lua_remove(l, -2); // Remove the table
50    lua_remove(l, -2); // Remove the original key
51}