mlua_sys/lua51/
compat.rs

1//! MLua compatibility layer for Lua 5.1/JIT
2//!
3//! Based on github.com/keplerproject/lua-compat-5.3
4
5use std::os::raw::{c_char, c_int, c_void};
6use std::{mem, ptr};
7
8use super::lauxlib::*;
9use super::lua::*;
10
11#[inline(always)]
12unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
13    while a < b {
14        lua_pushvalue(L, a);
15        lua_pushvalue(L, b);
16        lua_replace(L, a);
17        lua_replace(L, b);
18        a += 1;
19        b -= 1;
20    }
21}
22
23const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack
24const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack
25
26unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int {
27    let mut ar: lua_Debug = mem::zeroed();
28    let (mut li, mut le) = (1, 1);
29    // find an upper bound
30    while lua_getstack(L, le, &mut ar) != 0 {
31        li = le;
32        le *= 2;
33    }
34    // do a binary search
35    while li < le {
36        let m = (li + le) / 2;
37        if lua_getstack(L, m, &mut ar) != 0 {
38            li = m + 1
39        } else {
40            le = m;
41        }
42    }
43    le - 1
44}
45
46unsafe fn compat53_checkmode(
47    L: *mut lua_State,
48    mode: *const c_char,
49    modename: *const c_char,
50    err: c_int,
51) -> c_int {
52    unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char {
53        let mut st = s;
54        while *st != 0 && *st != c {
55            st = st.offset(1);
56        }
57        if *st == c {
58            st
59        } else {
60            ptr::null()
61        }
62    }
63
64    if !mode.is_null() && strchr(mode, *modename).is_null() {
65        lua_pushfstring(
66            L,
67            cstr!("attempt to load a %s chunk (mode is '%s')"),
68            modename,
69            mode,
70        );
71        return err;
72    }
73    LUA_OK
74}
75
76unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int {
77    if level == 0 || lua_istable(L, -1) == 0 {
78        return 0; // not found
79    }
80
81    lua_pushnil(L); // start 'next' loop
82    while lua_next(L, -2) != 0 {
83        // for each pair in table
84        if lua_type(L, -2) == LUA_TSTRING {
85            // ignore non-string keys
86            if lua_rawequal(L, objidx, -1) != 0 {
87                // found object?
88                lua_pop(L, 1); // remove value (but keep name)
89                return 1;
90            } else if compat53_findfield(L, objidx, level - 1) != 0 {
91                // try recursively
92                lua_remove(L, -2); // remove table (but keep name)
93                lua_pushliteral(L, c".");
94                lua_insert(L, -2); // place '.' between the two names
95                lua_concat(L, 3);
96                return 1;
97            }
98        }
99        lua_pop(L, 1); // remove value
100    }
101    0 // not found
102}
103
104unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> c_int {
105    let top = lua_gettop(L);
106    lua_getinfo(L, cstr!("f"), ar); // push function
107    lua_pushvalue(L, LUA_GLOBALSINDEX);
108    if compat53_findfield(L, top + 1, 2) != 0 {
109        lua_copy(L, -1, top + 1); // move name to proper place
110        lua_pop(L, 2); // remove pushed values
111        1
112    } else {
113        lua_settop(L, top); // remove function and global table
114        0
115    }
116}
117
118unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) {
119    if *(*ar).namewhat != b'\0' as c_char {
120        // is there a name?
121        lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
122    } else if *(*ar).what == b'm' as c_char {
123        // main?
124        lua_pushliteral(L, c"main chunk");
125    } else if *(*ar).what == b'C' as c_char {
126        if compat53_pushglobalfuncname(L, ar) != 0 {
127            lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
128            lua_remove(L, -2); // remove name
129        } else {
130            lua_pushliteral(L, c"?");
131        }
132    } else {
133        lua_pushfstring(
134            L,
135            cstr!("function <%s:%d>"),
136            (*ar).short_src.as_ptr(),
137            (*ar).linedefined,
138        );
139    }
140}
141
142//
143// lua ported functions
144//
145
146#[inline(always)]
147pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
148    if idx < 0 && idx > LUA_REGISTRYINDEX {
149        idx += lua_gettop(L) + 1;
150    }
151    idx
152}
153
154pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
155    idx = lua_absindex(L, idx);
156    if n > 0 {
157        // Faster version
158        for _ in 0..n {
159            lua_insert(L, idx);
160        }
161        return;
162    }
163    let n_elems = lua_gettop(L) - idx + 1;
164    if n < 0 {
165        n += n_elems;
166    }
167    if n > 0 && n < n_elems {
168        luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
169        n = n_elems - n;
170        compat53_reverse(L, idx, idx + n - 1);
171        compat53_reverse(L, idx + n, idx + n_elems - 1);
172        compat53_reverse(L, idx, idx + n_elems - 1);
173    }
174}
175
176#[inline(always)]
177pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
178    let abs_to = lua_absindex(L, toidx);
179    luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
180    lua_pushvalue(L, fromidx);
181    lua_replace(L, abs_to);
182}
183
184#[inline(always)]
185pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
186    if lua_type(L, idx) == LUA_TNUMBER {
187        let n = lua_tonumber(L, idx);
188        let i = lua_tointeger(L, idx);
189        // Lua 5.3+ returns "false" for `-0.0`
190        if n.to_bits() == (i as lua_Number).to_bits() {
191            return 1;
192        }
193    }
194    0
195}
196
197#[inline(always)]
198pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
199    lua_tointegerx(L, i, ptr::null_mut())
200}
201
202#[inline(always)]
203pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
204    let n = lua_tonumber(L, i);
205    if !isnum.is_null() {
206        *isnum = (n != 0.0 || lua_isnumber(L, i) != 0) as c_int;
207    }
208    n
209}
210
211#[inline(always)]
212pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
213    let mut ok = 0;
214    let n = lua_tonumberx(L, i, &mut ok);
215    let n_int = n as lua_Integer;
216    if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
217        if !isnum.is_null() {
218            *isnum = 1;
219        }
220        return n_int;
221    }
222    if !isnum.is_null() {
223        *isnum = 0;
224    }
225    0
226}
227
228#[inline(always)]
229pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
230    lua_objlen(L, idx)
231}
232
233#[inline(always)]
234pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
235    if l == 0 {
236        lua_pushlstring_(L, cstr!(""), 0);
237    } else {
238        lua_pushlstring_(L, s, l);
239    }
240    lua_tostring(L, -1)
241}
242
243#[inline(always)]
244pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
245    lua_pushstring_(L, s);
246    lua_tostring(L, -1)
247}
248
249#[inline(always)]
250pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
251    lua_getglobal_(L, var);
252    lua_type(L, -1)
253}
254
255#[inline(always)]
256pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
257    lua_gettable_(L, idx);
258    lua_type(L, -1)
259}
260
261#[inline(always)]
262pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
263    lua_getfield_(L, idx, k);
264    lua_type(L, -1)
265}
266
267#[inline(always)]
268pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
269    idx = lua_absindex(L, idx);
270    lua_pushinteger(L, n);
271    lua_gettable(L, idx)
272}
273
274#[inline(always)]
275pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
276    lua_rawget_(L, idx);
277    lua_type(L, -1)
278}
279
280#[inline(always)]
281pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
282    let n = n.try_into().expect("cannot convert index to lua_Integer");
283    lua_rawgeti_(L, idx, n);
284    lua_type(L, -1)
285}
286
287#[inline(always)]
288pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
289    let abs_i = lua_absindex(L, idx);
290    lua_pushlightuserdata(L, p as *mut c_void);
291    lua_rawget(L, abs_i)
292}
293
294#[inline(always)]
295pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
296    lua_getfenv(L, idx);
297    lua_type(L, -1)
298}
299
300#[inline(always)]
301pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
302    luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
303    idx = lua_absindex(L, idx);
304    lua_pushinteger(L, n);
305    lua_insert(L, -2);
306    lua_settable(L, idx);
307}
308
309#[inline(always)]
310pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
311    let n = n.try_into().expect("cannot convert index from lua_Integer");
312    lua_rawseti_(L, idx, n)
313}
314
315#[inline(always)]
316pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
317    let abs_i = lua_absindex(L, idx);
318    luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
319    lua_pushlightuserdata(L, p as *mut c_void);
320    lua_insert(L, -2);
321    lua_rawset(L, abs_i);
322}
323
324#[inline(always)]
325pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
326    luaL_checktype(L, -1, LUA_TTABLE);
327    lua_setfenv(L, idx);
328}
329
330#[inline(always)]
331pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
332    lua_dump_(L, writer, data)
333}
334
335#[inline(always)]
336pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
337    match lua_type(L, idx) {
338        LUA_TSTRING => {
339            lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
340        }
341        LUA_TTABLE => {
342            if luaL_callmeta(L, idx, cstr!("__len")) == 0 {
343                lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
344            }
345        }
346        LUA_TUSERDATA if luaL_callmeta(L, idx, cstr!("__len")) != 0 => {}
347        _ => {
348            luaL_error(
349                L,
350                cstr!("attempt to get length of a %s value"),
351                lua_typename(L, lua_type(L, idx)),
352            );
353        }
354    }
355}
356
357#[inline(always)]
358pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
359    lua_pushvalue(L, LUA_GLOBALSINDEX);
360}
361
362#[inline(always)]
363pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
364    let ret = lua_resume_(L, narg);
365    if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
366        *nres = lua_gettop(L);
367    }
368    ret
369}
370
371//
372// lauxlib ported functions
373//
374
375#[inline(always)]
376pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
377    if lua_checkstack(L, sz + LUA_MINSTACK) == 0 {
378        if !msg.is_null() {
379            luaL_error(L, cstr!("stack overflow (%s)"), msg);
380        } else {
381            lua_pushliteral(L, c"stack overflow");
382            lua_error(L);
383        }
384    }
385}
386
387#[inline(always)]
388pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
389    if luaL_getmetafield_(L, obj, e) != 0 {
390        lua_type(L, -1)
391    } else {
392        LUA_TNIL
393    }
394}
395
396#[inline(always)]
397pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
398    if luaL_newmetatable_(L, tname) != 0 {
399        lua_pushstring_(L, tname);
400        lua_setfield(L, -2, cstr!("__name"));
401        1
402    } else {
403        0
404    }
405}
406
407pub unsafe fn luaL_loadbufferenv(
408    L: *mut lua_State,
409    data: *const c_char,
410    size: usize,
411    name: *const c_char,
412    mode: *const c_char,
413    mut env: c_int,
414) -> c_int {
415    if env != 0 {
416        env = lua_absindex(L, env);
417    }
418    let status = luaL_loadbufferx(L, data, size, name, mode);
419    if status == LUA_OK && env != 0 {
420        lua_pushvalue(L, env);
421        lua_setfenv(L, -2);
422    }
423    status
424}
425
426#[inline(always)]
427pub unsafe fn luaL_loadbufferx(
428    L: *mut lua_State,
429    buff: *const c_char,
430    sz: usize,
431    name: *const c_char,
432    mode: *const c_char,
433) -> c_int {
434    let status = if sz > 0 && *buff as u8 == LUA_SIGNATURE[0] {
435        compat53_checkmode(L, mode, cstr!("binary"), LUA_ERRSYNTAX)
436    } else {
437        compat53_checkmode(L, mode, cstr!("text"), LUA_ERRSYNTAX)
438    };
439    if status != LUA_OK {
440        return status;
441    }
442    luaL_loadbuffer(L, buff, sz, name)
443}
444
445#[inline(always)]
446pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
447    let mut isnum = 0;
448    luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
449    lua_len(L, idx);
450    let res = lua_tointegerx(L, -1, &mut isnum);
451    lua_pop(L, 1);
452    if isnum == 0 {
453        luaL_error(L, cstr!("object length is not an integer"));
454    }
455    res
456}
457
458pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
459    let mut ar: lua_Debug = mem::zeroed();
460    let top = lua_gettop(L);
461    let numlevels = compat53_countlevels(L1);
462    let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
463        COMPAT53_LEVELS1
464    } else {
465        0
466    };
467
468    if !msg.is_null() {
469        lua_pushfstring(L, cstr!("%s\n"), msg);
470    }
471    lua_pushliteral(L, c"stack traceback:");
472    while lua_getstack(L1, level, &mut ar) != 0 {
473        level += 1;
474        if level == mark {
475            // too many levels?
476            lua_pushliteral(L, c"\n\t..."); // add a '...'
477            level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
478        } else {
479            lua_getinfo(L1, cstr!("Slnt"), &mut ar);
480            lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
481            if ar.currentline > 0 {
482                lua_pushfstring(L, cstr!("%d:"), ar.currentline);
483            }
484            lua_pushliteral(L, c" in ");
485            compat53_pushfuncname(L, &mut ar);
486            lua_concat(L, lua_gettop(L) - top);
487        }
488    }
489    lua_concat(L, lua_gettop(L) - top);
490}
491
492pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
493    idx = lua_absindex(L, idx);
494    if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
495        match lua_type(L, idx) {
496            LUA_TNIL => {
497                lua_pushliteral(L, c"nil");
498            }
499            LUA_TSTRING | LUA_TNUMBER => {
500                lua_pushvalue(L, idx);
501            }
502            LUA_TBOOLEAN => {
503                if lua_toboolean(L, idx) == 0 {
504                    lua_pushliteral(L, c"false");
505                } else {
506                    lua_pushliteral(L, c"true");
507                }
508            }
509            t => {
510                let tt = luaL_getmetafield(L, idx, cstr!("__name"));
511                let name = if tt == LUA_TSTRING {
512                    lua_tostring(L, -1)
513                } else {
514                    lua_typename(L, t)
515                };
516                lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
517                if tt != LUA_TNIL {
518                    lua_replace(L, -2); // remove '__name'
519                }
520            }
521        };
522    } else if lua_isstring(L, -1) == 0 {
523        luaL_error(L, cstr!("'__tostring' must return a string"));
524    }
525    lua_tolstring(L, -1, len)
526}
527
528#[inline(always)]
529pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
530    luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
531    luaL_getmetatable(L, tname);
532    lua_setmetatable(L, -2);
533}
534
535pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
536    let abs_i = lua_absindex(L, idx);
537    luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
538    lua_pushstring_(L, fname);
539    if lua_gettable(L, abs_i) == LUA_TTABLE {
540        return 1;
541    }
542    lua_pop(L, 1);
543    lua_newtable(L);
544    lua_pushstring_(L, fname);
545    lua_pushvalue(L, -2);
546    lua_settable(L, abs_i);
547    0
548}
549
550pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
551    luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
552    luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
553    if lua_getfield(L, -1, modname) == LUA_TNIL {
554        lua_pop(L, 1);
555        lua_pushcfunction(L, openf);
556        lua_pushstring(L, modname);
557        #[cfg(feature = "lua51")]
558        {
559            lua_call(L, 1, 1);
560            lua_pushvalue(L, -1);
561            lua_setfield(L, -3, modname);
562        }
563        #[cfg(feature = "luajit")]
564        {
565            lua_call(L, 1, 0);
566            lua_getfield(L, -1, modname);
567        }
568    }
569    if glb != 0 {
570        lua_pushvalue(L, -1);
571        lua_setglobal(L, modname);
572    } else {
573        lua_pushnil(L);
574        lua_setglobal(L, modname);
575    }
576    lua_replace(L, -2);
577}