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