diff options
author | Alex Orlenko <zxteam@protonmail.com> | 2019-10-14 22:21:30 +0100 |
---|---|---|
committer | Alex Orlenko <zxteam@protonmail.com> | 2019-10-17 17:05:42 +0100 |
commit | c4fd7a9faf35fb9435d0eea63fcb586c1b5da9fb (patch) | |
tree | ca48c33057b43a651d8b9baaa4de508f54492801 /src/ffi/compat53.rs | |
parent | 676ffc0dfd93480e7f6b183163c20c7484011312 (diff) | |
download | mlua-c4fd7a9faf35fb9435d0eea63fcb586c1b5da9fb.zip |
Lua 5.1 support
Diffstat (limited to 'src/ffi/compat53.rs')
-rw-r--r-- | src/ffi/compat53.rs | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/src/ffi/compat53.rs b/src/ffi/compat53.rs new file mode 100644 index 0000000..2b58ebc --- /dev/null +++ b/src/ffi/compat53.rs @@ -0,0 +1,668 @@ +// The MIT License (MIT) +// +// Copyright (c) 2019 A. Orlenko +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Based on github.com/keplerproject/lua-compat-5.3 + +use std::mem; +use std::os::raw::{c_char, c_int, c_void}; +use std::ptr; + +use super::lauxlib::{ + luaL_Reg, luaL_callmeta, luaL_checkstack, luaL_checktype, luaL_error, luaL_getmetafield_51, + luaL_getmetatable, luaL_loadbuffer, luaL_newmetatable_51, +}; + +use super::lua::{ + self, lua_CFunction, lua_Debug, lua_Integer, lua_Number, lua_State, lua_call, lua_concat, + lua_createtable, lua_equal, lua_error, lua_getfenv, lua_getfield_51, lua_getinfo, + lua_getmetatable, lua_getstack, lua_gettable_51, lua_gettop, lua_insert, lua_isnumber, + lua_isstring, lua_istable, lua_lessthan, lua_newtable, lua_newuserdata, lua_next, lua_objlen, + lua_pop, lua_pushboolean, lua_pushcclosure, lua_pushcfunction, lua_pushfstring, + lua_pushinteger, lua_pushlightuserdata, lua_pushliteral, lua_pushlstring_51, lua_pushnil, + lua_pushnumber, lua_pushstring_51, lua_pushthread, lua_pushvalue, lua_rawequal, lua_rawget_51, + lua_rawgeti_51, lua_rawset, lua_remove, lua_replace, lua_resume_51, lua_setfenv, lua_setfield, + lua_setglobal, lua_setmetatable, lua_settable, lua_settop, lua_toboolean, lua_tointeger, + lua_tolstring, lua_tonumber, lua_topointer, lua_tostring, lua_touserdata, lua_type, + lua_typename, +}; + +unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) { + while a < b { + lua_pushvalue(L, a); + lua_pushvalue(L, b); + lua_replace(L, a); + lua_replace(L, b); + a += 1; + b -= 1; + } +} + +const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack +const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack + +unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int { + let mut ar: lua_Debug = mem::zeroed(); + let (mut li, mut le) = (1, 1); + // find an upper bound + while lua_getstack(L, le, &mut ar) != 0 { + li = le; + le *= 2; + } + // do a binary search + while li < le { + let m = (li + le) / 2; + if lua_getstack(L, m, &mut ar) != 0 { + li = m + 1 + } else { + le = m; + } + } + le - 1 +} + +unsafe fn compat53_checkmode( + L: *mut lua_State, + mode: *const c_char, + modename: *const c_char, + err: c_int, +) -> c_int { + unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char { + let mut st = s; + while *st != 0 && *st != c { + st = st.offset(1); + } + if *st == c { + st + } else { + ptr::null() + } + } + + if mode != ptr::null() && strchr(mode, *modename) == ptr::null() { + lua_pushfstring( + L, + cstr!("attempt to load a %s chunk (mode is '%s')"), + modename, + mode, + ); + return err; + } + lua::LUA_OK +} + +unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int { + if level == 0 || lua_istable(L, -1) == 0 { + return 0; // not found + } + + lua_pushnil(L); // start 'next' loop + while lua_next(L, -2) != 0 { + // for each pair in table + if lua_type(L, -2) == lua::LUA_TSTRING { + // ignore non-string keys + if lua_rawequal(L, objidx, -1) != 0 { + // found object? + lua_pop(L, 1); // remove value (but keep name) + return 1; + } else if compat53_findfield(L, objidx, level - 1) != 0 { + // try recursively + lua_remove(L, -2); // remove table (but keep name) + lua_pushliteral(L, "."); + lua_insert(L, -2); // place '.' between the two names + lua_concat(L, 3); + return 1; + } + } + lua_pop(L, 1); // remove value + } + return 0; // not found +} + +unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> c_int { + let top = lua_gettop(L); + lua_getinfo(L, cstr!("f"), ar); // push function + lua_pushvalue(L, lua::LUA_GLOBALSINDEX); + if compat53_findfield(L, top + 1, 2) != 0 { + lua_copy(L, -1, top + 1); // move name to proper place + lua_pop(L, 2); // remove pushed values + return 1; + } else { + lua_settop(L, top); // remove function and global table + return 0; + } +} + +unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) { + if *(*ar).namewhat != b'\0' as c_char { + // is there a name? + lua_pushfstring(L, cstr!("function '%s'"), (*ar).name); + } else if *(*ar).what == b'm' as c_char { + // main? + lua_pushliteral(L, "main chunk"); + } else if *(*ar).what == b'C' as c_char { + if compat53_pushglobalfuncname(L, ar) != 0 { + lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1)); + lua_remove(L, -2); // remove name + } else { + lua_pushliteral(L, "?"); + } + } else { + lua_pushfstring( + L, + cstr!("function <%s:%d>"), + (*ar).short_src.as_ptr(), + (*ar).linedefined, + ); + } +} + +unsafe fn compat53_call_lua(L: *mut lua_State, code: &str, nargs: c_int, nret: c_int) { + lua_rawgetp(L, lua::LUA_REGISTRYINDEX, code.as_ptr() as *const c_void); + if lua_type(L, -1) != lua::LUA_TFUNCTION { + lua_pop(L, 1); + if luaL_loadbuffer( + L, + code.as_ptr() as *const c_char, + code.as_bytes().len(), + cstr!("=none"), + ) != 0 + { + lua_error(L); + } + lua_pushvalue(L, -1); + lua_rawsetp(L, lua::LUA_REGISTRYINDEX, code.as_ptr() as *const c_void); + } + lua_insert(L, -nargs - 1); + lua_call(L, nargs, nret); +} + +// +// lua ported functions +// + +#[inline(always)] +pub fn lua_upvalueindex(i: c_int) -> c_int { + lua::LUA_GLOBALSINDEX - i +} + +pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int { + if idx < 0 && idx > lua::LUA_REGISTRYINDEX { + idx += lua_gettop(L) + 1; + } + idx +} + +pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) { + idx = lua_absindex(L, idx); + let n_elems = lua_gettop(L) - idx + 1; + if n < 0 { + n += n_elems; + } + if n > 0 && n < n_elems { + luaL_checkstack(L, 2, cstr!("not enough stack slots available")); + n = n_elems - n; + compat53_reverse(L, idx, idx + n - 1); + compat53_reverse(L, idx + n, idx + n_elems - 1); + compat53_reverse(L, idx, idx + n_elems - 1); + } +} + +pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) { + let abs_to = lua_absindex(L, toidx); + luaL_checkstack(L, 1, cstr!("not enough stack slots")); + lua_pushvalue(L, fromidx); + lua_replace(L, abs_to); +} + +pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int { + if lua_type(L, idx) == lua::LUA_TNUMBER { + let n = lua_tonumber(L, idx); + let i = lua_tointeger(L, idx); + if i as f64 == n { + return 1; + } + } + return 0; +} + +pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number { + let n = lua_tonumber(L, i); + if isnum != ptr::null_mut() { + *isnum = if n != 0.0 || lua_isnumber(L, i) != 0 { + 1 + } else { + 0 + }; + } + return n; +} + +pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer { + let mut ok = 0; + let n = lua_tonumberx(L, i, &mut ok); + if ok != 0 { + if n == n as lua_Integer as lua_Number { + if isnum != ptr::null_mut() { + *isnum = 1; + } + return n as lua_Integer; + } + } + if isnum != ptr::null_mut() { + *isnum = 0; + } + return 0; +} + +pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize { + lua_objlen(L, idx) +} + +pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, op: c_int) -> c_int { + match op { + lua::LUA_OPEQ => lua_equal(L, idx1, idx2), + lua::LUA_OPLT => lua_lessthan(L, idx1, idx2), + lua::LUA_OPLE => { + luaL_checkstack(L, 5, cstr!("not enough stack slots")); + idx1 = lua_absindex(L, idx1); + idx2 = lua_absindex(L, idx2); + lua_pushvalue(L, idx1); + lua_pushvalue(L, idx2); + compat53_call_lua(L, "local a,b=...\nreturn a<=b\n", 2, 1); + let result = lua_toboolean(L, -1); + lua_pop(L, 1); + result + } + _ => luaL_error(L, cstr!("invalid 'op' argument for lua_compare")), + } +} + +pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char { + if l == 0 { + lua_pushlstring_51(L, cstr!(""), 0); + } else { + lua_pushlstring_51(L, s, l); + } + lua_tostring(L, -1) +} + +pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char { + lua_pushstring_51(L, s); + lua_tostring(L, -1) +} + +pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int { + lua_gettable_51(L, idx); + lua_type(L, -1) +} + +pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int { + lua_getfield_51(L, idx, k); + lua_type(L, -1) +} + +pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int { + idx = lua_absindex(L, idx); + lua_pushinteger(L, n); + lua_gettable(L, idx); + lua_type(L, -1) +} + +// A new version which returns c_int +pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int { + lua_rawget_51(L, idx); + lua_type(L, -1) +} + +// A new version which returns c_int +pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int { + lua_rawgeti_51(L, idx, n); + lua_type(L, -1) +} + +pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int { + let abs_i = lua_absindex(L, idx); + lua_pushlightuserdata(L, p as *mut c_void); + lua_rawget(L, abs_i); + lua_type(L, -1) +} + +pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int { + lua_getfenv(L, idx); + lua_type(L, -1) +} + +pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) { + luaL_checkstack(L, 1, cstr!("not enough stack slots available")); + idx = lua_absindex(L, idx); + lua_pushinteger(L, n); + lua_insert(L, -2); + lua_settable(L, idx); +} + +pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) { + let abs_i = lua_absindex(L, idx); + luaL_checkstack(L, 1, cstr!("not enough stack slots")); + lua_pushlightuserdata(L, p as *mut c_void); + lua_insert(L, -2); + lua_rawset(L, abs_i); +} + +pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) { + luaL_checktype(L, -1, lua::LUA_TTABLE); + lua_setfenv(L, idx); +} + +pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int) -> c_int { + lua_resume_51(L, narg) +} + +pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) { + match lua_type(L, idx) { + lua::LUA_TSTRING => { + lua_pushnumber(L, lua_objlen(L, idx) as lua_Number); + } + lua::LUA_TTABLE => { + if luaL_callmeta(L, idx, cstr!("__len")) == 0 { + lua_pushnumber(L, lua_objlen(L, idx) as lua_Number); + } + } + lua::LUA_TUSERDATA if luaL_callmeta(L, idx, cstr!("__len")) != 0 => {} + _ => { + luaL_error( + L, + cstr!("attempt to get length of a %s value"), + lua_typename(L, lua_type(L, idx)), + ); + } + } +} + +// TODO: lua_stringtonumber + +pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void { + use super::glue::LUA_EXTRASPACE; + + luaL_checkstack(L, 4, cstr!("not enough stack slots available")); + lua_pushliteral(L, "__compat53_extraspace"); + lua_pushvalue(L, -1); + lua_rawget(L, lua::LUA_REGISTRYINDEX); + if lua_istable(L, -1) == 0 { + lua_pop(L, 1); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 1); + lua_pushliteral(L, "k"); + lua_setfield(L, -2, cstr!("__mode")); + lua_setmetatable(L, -2); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, lua::LUA_REGISTRYINDEX); + } + lua_replace(L, -2); + let is_main = lua_pushthread(L); + lua_rawget(L, -2); + let mut _ptr = lua_touserdata(L, -1); + if _ptr == ptr::null_mut() { + lua_pop(L, 1); + _ptr = lua_newuserdata(L, LUA_EXTRASPACE as usize); + if is_main != 0 { + // mem::size_of::<c_void>() == 1 + ptr::write_bytes(_ptr, 0, LUA_EXTRASPACE as usize); + lua_pushthread(L); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + lua_pushboolean(L, 1); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } else { + lua_pushboolean(L, 1); + lua_rawget(L, -3); + let mptr = lua_touserdata(L, -1); + if mptr != ptr::null_mut() { + ptr::copy_nonoverlapping(mptr, _ptr, LUA_EXTRASPACE as usize) + } else { + ptr::write_bytes(_ptr, 0, LUA_EXTRASPACE as usize); + } + lua_pop(L, 1); + lua_pushthread(L); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + } + lua_pop(L, 2); + return _ptr; +} + +#[inline(always)] +pub unsafe fn lua_pushglobaltable(L: *mut lua_State) { + lua_pushvalue(L, lua::LUA_GLOBALSINDEX); +} + +// +// lauxlib ported functions +// + +pub unsafe fn luaL_checkversion(_L: *mut lua_State) { + // Void +} + +pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int { + if luaL_getmetafield_51(L, obj, e) != 0 { + lua_type(L, -1) + } else { + lua::LUA_TNIL + } +} + +pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int { + if luaL_newmetatable_51(L, tname) != 0 { + lua_pushstring(L, tname); + lua_setfield(L, -2, cstr!("__name")); + 1 + } else { + 0 + } +} + +pub unsafe fn luaL_loadbufferx( + L: *mut lua_State, + buff: *const c_char, + sz: usize, + name: *const c_char, + mode: *const c_char, +) -> c_int { + let status = if sz > 0 && *buff as u8 == lua::LUA_SIGNATURE[0] { + compat53_checkmode(L, mode, cstr!("binary"), lua::LUA_ERRSYNTAX) + } else { + compat53_checkmode(L, mode, cstr!("text"), lua::LUA_ERRSYNTAX) + }; + if status != lua::LUA_OK { + return status; + } + luaL_loadbuffer(L, buff, sz, name) +} + +pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer { + let mut isnum = 0; + luaL_checkstack(L, 1, cstr!("not enough stack slots")); + lua_len(L, idx); + let res = lua_tointegerx(L, -1, &mut isnum); + lua::lua_pop(L, 1); + if isnum == 0 { + luaL_error(L, cstr!("object length is not an integer")); + } + res +} + +pub unsafe fn luaL_traceback( + L: *mut lua_State, + L1: *mut lua_State, + msg: *const c_char, + mut level: c_int, +) { + let mut ar: lua_Debug = std::mem::zeroed(); + let top = lua_gettop(L); + let numlevels = compat53_countlevels(L1); + let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { + COMPAT53_LEVELS1 + } else { + 0 + }; + + if msg != ptr::null() { + lua_pushfstring(L, cstr!("%s\n"), msg); + } + lua_pushliteral(L, "stack traceback:"); + while lua_getstack(L1, level, &mut ar) != 0 { + level += 1; + if level == mark { + // too many levels? + lua_pushliteral(L, "\n\t..."); // add a '...' + level = numlevels - COMPAT53_LEVELS2; // and skip to last ones + } else { + lua_getinfo(L1, cstr!("Slnt"), &mut ar); + lua_pushfstring(L, cstr!("\n\t%s:"), cstr!("ok") /*ar.short_src*/); + if ar.currentline > 0 { + lua_pushfstring(L, cstr!("%d:"), ar.currentline); + } + lua_pushliteral(L, " in "); + compat53_pushfuncname(L, &mut ar); + lua_concat(L, lua_gettop(L) - top); + } + } + lua_concat(L, lua_gettop(L) - top); +} + +pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char { + if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 { + let t = lua_type(L, idx); + match t { + lua::LUA_TNIL => { + lua_pushliteral(L, "nil"); + } + lua::LUA_TSTRING | lua::LUA_TNUMBER => { + lua_pushvalue(L, idx); + } + lua::LUA_TBOOLEAN => { + if lua_toboolean(L, idx) == 0 { + lua_pushliteral(L, "false"); + } else { + lua_pushliteral(L, "true"); + } + } + _ => { + let tt = luaL_getmetafield(L, idx, cstr!("__name")); + let name = if tt == lua::LUA_TSTRING { + lua_tostring(L, -1) + } else { + lua_typename(L, t) + }; + lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx)); + if tt != lua::LUA_TNIL { + lua_replace(L, -2); + } + } + }; + } else { + if lua_isstring(L, -1) == 0 { + luaL_error(L, cstr!("'__tostring' must return a string")); + } + } + lua_tolstring(L, -1, len) +} + +pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) { + luaL_checkstack(L, 1, cstr!("not enough stack slots")); + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + +pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void { + let mut p = lua_touserdata(L, i); + luaL_checkstack(L, 2, cstr!("not enough stack slots")); + if p == ptr::null_mut() || lua_getmetatable(L, i) == 0 { + return ptr::null_mut(); + } else { + luaL_getmetatable(L, tname); + let res = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + if res == 0 { + p = ptr::null_mut(); + } + } + return p; +} + +pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_int) { + luaL_checkstack(L, nup + 1, cstr!("too many upvalues")); + while (*l).name != ptr::null() { + // fill the table with given functions + l = l.offset(1); + lua_pushstring(L, (*l).name); + for _ in 0..nup { + // copy upvalues to the top + lua_pushvalue(L, -(nup + 1)); + } + lua_pushcclosure(L, (*l).func, nup); // closure with those upvalues + lua_settable(L, -(nup + 3)); // table must be below the upvalues, the name and the closure + } + lua_pop(L, nup); // remove upvalues +} + +pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int { + let abs_i = lua_absindex(L, idx); + luaL_checkstack(L, 3, cstr!("not enough stack slots")); + lua_pushstring(L, fname); + lua_gettable(L, abs_i); + if lua_istable(L, -1) != 0 { + return 1; + } + lua_pop(L, 1); + lua_newtable(L); + lua_pushstring(L, fname); + lua_pushvalue(L, -2); + lua_settable(L, abs_i); + return 0; +} + +pub unsafe fn luaL_requiref( + L: *mut lua_State, + modname: *const c_char, + openf: lua_CFunction, + glb: c_int, +) { + luaL_checkstack(L, 3, cstr!("not enough stack slots available")); + luaL_getsubtable(L, lua::LUA_REGISTRYINDEX, cstr!("_LOADED")); + if lua_getfield(L, -1, modname) == lua::LUA_TNIL { + lua_pop(L, 1); + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); + lua_call(L, 1, 1); + lua_pushvalue(L, -1); + lua_setfield(L, -3, modname); + } + if glb != 0 { + lua_pushvalue(L, -1); + lua_setglobal(L, modname); + } + lua_replace(L, -2); +} |