diff --git a/Cargo.toml b/Cargo.toml index e05b055..75b3223 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "mlua" version = "0.1.0" authors = ["Aleksandr Orlenko ", "kyren "] edition = "2018" -description = "High level bindings to Lua 5.3 for writing modules" +description = "High level bindings to Lua 5.3/5.1 for writing modules" repository = "https://github.com/khvzak/mlua" documentation = "https://docs.rs/mlua" readme = "README.md" @@ -18,6 +18,10 @@ members = [ "mlua_derive", ] +[features] +default = ["lua53"] +lua53 = [] + [dependencies] num-traits = { version = "0.2.6" } bstr = { version = "0.2", features = ["std"], default_features = false } diff --git a/build.rs b/build.rs index 7847bd8..a675114 100644 --- a/build.rs +++ b/build.rs @@ -45,8 +45,13 @@ fn use_custom_lua>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> R } } + let mut static_link = ""; + if env::var("LUA_LINK").unwrap_or(String::new()) == "static" { + static_link = "static="; + } + println!("cargo:rustc-link-search=native={}", lib_dir.as_ref()); - println!("cargo:rustc-link-lib=static={}", lua_lib.as_ref()); + println!("cargo:rustc-link-lib={}{}", static_link, lua_lib.as_ref()); Ok(version_found) } @@ -91,6 +96,7 @@ fn main() { println!("cargo:rerun-if-env-changed=LUA_INC"); println!("cargo:rerun-if-env-changed=LUA_LIB"); println!("cargo:rerun-if-env-changed=LUA_LIB_NAME"); + println!("cargo:rerun-if-env-changed=LUA_LINK"); println!("cargo:rerun-if-changed=src/ffi/glue/glue.c"); if include_dir != "" && lib_dir != "" && lua_lib != "" { @@ -101,10 +107,35 @@ fn main() { // Find lua via pkg-config - let lua = pkg_config::Config::new() - .range_version((Bound::Included("5.3"), Bound::Excluded("5.4"))) - .probe("lua") - .unwrap(); + #[cfg(feature = "lua53")] + { + let mut lua = pkg_config::Config::new() + .range_version((Bound::Included("5.3"), Bound::Excluded("5.4"))) + .probe("lua"); - build_glue(&lua.include_paths); + if lua.is_err() { + lua = pkg_config::Config::new().probe("lua5.3"); + } + + match lua { + Ok(lua) => build_glue(&lua.include_paths), + Err(err) => panic!(err), + }; + } + + #[cfg(not(feature = "lua53"))] + { + let mut lua = pkg_config::Config::new() + .range_version((Bound::Included("5.1"), Bound::Excluded("5.2"))) + .probe("lua"); + + if lua.is_err() { + lua = pkg_config::Config::new().probe("lua5.1"); + } + + match lua { + Ok(lua) => build_glue(&lua.include_paths), + Err(err) => panic!(err), + }; + } } diff --git a/src/error.rs b/src/error.rs index f670d1b..8431503 100644 --- a/src/error.rs +++ b/src/error.rs @@ -31,6 +31,7 @@ pub enum Error { /// Lua garbage collector error, aka `LUA_ERRGCMM`. /// /// The Lua VM returns this error when there is an error running a `__gc` metamethod. + #[cfg(feature = "lua53")] GarbageCollectorError(StdString), /// A mutable callback has triggered Lua code that has called the same mutable callback again. /// @@ -137,6 +138,7 @@ impl fmt::Display for Error { Error::MemoryError(ref msg) => { write!(fmt, "memory error: {}", msg) } + #[cfg(feature = "lua53")] Error::GarbageCollectorError(ref msg) => { write!(fmt, "garbage collector error: {}", msg) } 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::() == 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); +} diff --git a/src/ffi/glue/glue.c b/src/ffi/glue/glue.c index 65249d6..d50bbd9 100644 --- a/src/ffi/glue/glue.c +++ b/src/ffi/glue/glue.c @@ -1,5 +1,6 @@ // The MIT License (MIT) // +// Copyright (c) 2019 A. Orlenko // Copyright (c) 2014 J.C. Moyer // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,6 +30,10 @@ #include #include +#ifndef LUA_EXTRASPACE +#define LUA_EXTRASPACE (sizeof(void*)) +#endif + // Macros taken from https://gcc.gnu.org/onlinedocs/cpp/Stringification.html #define xstr(s) str(s) #define str(s) #s @@ -43,23 +48,30 @@ typedef struct rs_item { }; } rs_item; -#define TY_INT 0 -#define TY_LUAINT 1 -#define TY_STR 2 -#define TY_TYPE 3 -#define TY_COMMENT 4 -#define TY_RAW 5 +#define TY_INT 0 #define RS_INT(name, val) \ { TY_INT, name, .int_val = val } + +#if LUA_VERSION_NUM == 503 +#define TY_LUAINT 1 #define RS_LUAINT(name, val) \ { TY_LUAINT, name, .lua_int_val = val } +#endif + +#define TY_STR 2 #define RS_STR(name, val) \ { TY_STR, name, .str_val = val } + +#define TY_TYPE 3 #define RS_TYPE(name, val) \ { TY_TYPE, name, .str_val = val } + +#define TY_COMMENT 4 #define RS_COMMENT(val) \ { TY_COMMENT, NULL, .str_val = val } + +#define TY_RAW 5 #define RS_RAW(val) \ { TY_RAW, NULL, .str_val = val } @@ -102,7 +114,7 @@ size_t escape(const char *in, char *out, size_t szout) { size_t written = 0; char cur; - while (cur = *in++) { + while ((cur = *in++)) { switch (cur) { case '\\': if (!try_write(&out, cur, 2, &written, szout)) @@ -127,10 +139,12 @@ int write_int_item(FILE *f, const char *name, int value) { return fprintf(f, "pub const %s: c_int = %d;\n", name, value); } +#if LUA_VERSION_NUM == 503 int write_lua_int_item(FILE *f, const char *name, LUA_INTEGER value) { return fprintf(f, "pub const %s: LUA_INTEGER = " LUA_INTEGER_FMT ";\n", name, value); } +#endif int write_str_item(FILE *f, const char *name, const char *value) { size_t len = strlen(value); @@ -157,8 +171,10 @@ int write_item(FILE *f, const rs_item *c) { switch (c->type) { case TY_INT: return write_int_item(f, c->name, c->int_val); +#if LUA_VERSION_NUM == 503 case TY_LUAINT: return write_lua_int_item(f, c->name, c->lua_int_val); +#endif case TY_STR: return write_str_item(f, c->name, c->str_val); case TY_TYPE: @@ -205,43 +221,37 @@ int main(int argc, const char **argv) { // == luaconf.h ========================================================== RS_COMMENT("luaconf.h"), - RS_STR("LUA_VDIR", LUA_VDIR), RS_STR("LUA_PATH_DEFAULT", LUA_PATH_DEFAULT), RS_STR("LUA_CPATH_DEFAULT", LUA_CPATH_DEFAULT), RS_STR("LUA_DIRSEP", LUA_DIRSEP), RS_INT("LUA_EXTRASPACE", LUA_EXTRASPACE), RS_INT("LUA_IDSIZE", LUA_IDSIZE), - // RS_INT("LUAI_MAXSHORTLEN", LUAI_MAXSHORTLEN), - // RS_TYPE("LUA_KCONTEXT", xstr(LUA_KCONTEXT)), - RS_INT("LUAI_BITSINT", LUAI_BITSINT), - // LUA_INT32? LUAI_UMEM? LUAI_MEM? - RS_INT("LUAI_MAXSTACK", LUAI_MAXSTACK), RS_INT("LUAL_BUFFERSIZE", LUAL_BUFFERSIZE), RS_TYPE("LUA_NUMBER", sizeof(LUA_NUMBER) > sizeof(float) ? "c_double" : "c_float"), - RS_TYPE("LUA_UNSIGNED", rs_uint_type(sizeof(LUA_UNSIGNED))), RS_TYPE("LUA_INTEGER", rs_int_type(sizeof(LUA_INTEGER))), - RS_LUAINT("LUA_MAXINTEGER", LUA_MAXINTEGER), - RS_LUAINT("LUA_MININTEGER", LUA_MININTEGER), +#if LUA_VERSION_NUM == 503 + RS_TYPE("LUA_UNSIGNED", rs_uint_type(sizeof(LUA_UNSIGNED))), +#endif // == lua.h ============================================================== RS_COMMENT("lua.h"), - RS_STR("LUA_VERSION_MAJOR", LUA_VERSION_MAJOR), - RS_STR("LUA_VERSION_MINOR", LUA_VERSION_MINOR), RS_INT("LUA_VERSION_NUM", LUA_VERSION_NUM), - RS_STR("LUA_VERSION_RELEASE", LUA_VERSION_RELEASE), RS_STR("LUA_VERSION", LUA_VERSION), RS_STR("LUA_RELEASE", LUA_RELEASE), - RS_STR("LUA_COPYRIGHT", LUA_COPYRIGHT), - RS_STR("LUA_AUTHORS", LUA_AUTHORS), RS_INT("LUA_REGISTRYINDEX", LUA_REGISTRYINDEX), +#if LUA_VERSION_NUM == 501 + RS_INT("LUA_ENVIRONINDEX", LUA_ENVIRONINDEX), + RS_INT("LUA_GLOBALSINDEX", LUA_GLOBALSINDEX), +#endif // == lauxlib.h ========================================================== RS_COMMENT("lauxlib.h"), +#if LUA_VERSION_NUM == 503 RS_INT("LUAL_NUMSIZES", LUAL_NUMSIZES), - RS_STR("LUA_FILEHANDLE", LUA_FILEHANDLE), +#endif // == lualib.h =========================================================== @@ -251,8 +261,10 @@ int main(int argc, const char **argv) { RS_STR("LUA_IOLIBNAME", LUA_IOLIBNAME), RS_STR("LUA_OSLIBNAME", LUA_OSLIBNAME), RS_STR("LUA_STRLIBNAME", LUA_STRLIBNAME), +#if LUA_VERSION_NUM == 503 RS_STR("LUA_UTF8LIBNAME", LUA_UTF8LIBNAME), RS_STR("LUA_BITLIBNAME", LUA_BITLIBNAME), +#endif RS_STR("LUA_MATHLIBNAME", LUA_MATHLIBNAME), RS_STR("LUA_DBLIBNAME", LUA_DBLIBNAME), RS_STR("LUA_LOADLIBNAME", LUA_LOADLIBNAME), diff --git a/src/ffi/lauxlib.rs b/src/ffi/lauxlib.rs index 5a9c5c8..941755f 100644 --- a/src/ffi/lauxlib.rs +++ b/src/ffi/lauxlib.rs @@ -1,5 +1,6 @@ // The MIT License (MIT) // +// Copyright (c) 2019 A. Orlenko // Copyright (c) 2014 J.C. Moyer // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,10 +27,16 @@ use std::os::raw::{c_char, c_int, c_long, c_void}; use std::ptr; use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State}; -use super::luaconf::LUAL_BUFFERSIZE; +#[cfg(feature = "lua53")] pub use super::glue::LUAL_NUMSIZES; -pub use super::glue::LUA_FILEHANDLE; + +#[cfg(not(feature = "lua53"))] +pub use super::compat53::{ + luaL_checkversion, luaL_getmetafield, luaL_getsubtable, luaL_len, luaL_loadbufferx, + luaL_newmetatable, luaL_requiref, luaL_setfuncs, luaL_setmetatable, luaL_testudata, + luaL_tolstring, luaL_traceback, +}; // extra error code for 'luaL_load' pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1; @@ -40,6 +47,7 @@ pub struct luaL_Reg { pub func: lua_CFunction, } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn luaL_checkversion(L: *mut lua_State) { luaL_checkversion_( @@ -50,10 +58,17 @@ pub unsafe fn luaL_checkversion(L: *mut lua_State) { } extern "C" { + #[cfg(feature = "lua53")] pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize); + #[cfg(feature = "lua53")] pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "luaL_getmetafield"] + pub fn luaL_getmetafield_51(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; + pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; + #[cfg(feature = "lua53")] pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; pub fn luaL_argerror(L: *mut lua_State, arg: c_int, l: *const c_char) -> c_int; pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char; @@ -72,13 +87,20 @@ extern "C" { pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int); pub fn luaL_checkany(L: *mut lua_State, arg: c_int); + #[cfg(feature = "lua53")] pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "luaL_newmetatable"] + pub fn luaL_newmetatable_51(L: *mut lua_State, tname: *const c_char) -> c_int; + + #[cfg(feature = "lua53")] pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char); + #[cfg(feature = "lua53")] pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; pub fn luaL_where(L: *mut lua_State, lvl: c_int); - pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int; + pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !; // TODO: test this pub fn luaL_checkoption( @@ -88,7 +110,9 @@ extern "C" { lst: *const *const c_char, ) -> c_int; + #[cfg(feature = "lua53")] pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int; + #[cfg(feature = "lua53")] pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int; } @@ -100,16 +124,21 @@ extern "C" { pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int; pub fn luaL_unref(L: *mut lua_State, t: c_int, r: c_int); + #[cfg(feature = "lua53")] pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int; + #[cfg(not(feature = "lua53"))] + pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int; } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int { luaL_loadfilex(L, f, ptr::null()) } extern "C" { + #[cfg(feature = "lua53")] pub fn luaL_loadbufferx( L: *mut lua_State, buff: *const c_char, @@ -117,10 +146,18 @@ extern "C" { name: *const c_char, mode: *const c_char, ) -> c_int; + #[cfg(not(feature = "lua53"))] + pub fn luaL_loadbuffer( + L: *mut lua_State, + buff: *const c_char, + sz: usize, + name: *const c_char, + ) -> c_int; pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int; pub fn luaL_newstate() -> *mut lua_State; + #[cfg(feature = "lua53")] pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer; pub fn luaL_gsub( @@ -130,12 +167,16 @@ extern "C" { r: *const c_char, ) -> *const c_char; + #[cfg(feature = "lua53")] pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int); + #[cfg(feature = "lua53")] pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int; + #[cfg(feature = "lua53")] pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int); + #[cfg(feature = "lua53")] pub fn luaL_requiref( L: *mut lua_State, modname: *const c_char, @@ -238,6 +279,7 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) { // luaL_opt would be implemented here but it is undocumented, so it's omitted +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn luaL_loadbuffer( L: *mut lua_State, @@ -248,46 +290,6 @@ pub unsafe fn luaL_loadbuffer( luaL_loadbufferx(L, s, sz, n, ptr::null()) } -#[repr(C)] -pub struct luaL_Buffer { - pub b: *mut c_char, - pub size: usize, - pub n: usize, - pub L: *mut lua_State, - pub initb: [c_char; LUAL_BUFFERSIZE as usize], -} - -// TODO: Test this thoroughly -#[inline(always)] -pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) { - // (B)->n < (B) -> size || luaL_prepbuffsize((B), 1) - if (*B).n < (*B).size { - luaL_prepbuffsize(B, 1); - } - // (B)->b[(B)->n++] = (c) - let offset = (*B).b.offset((*B).n as isize); - ptr::write(offset, c); - (*B).n += 1; -} - -#[inline(always)] -pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, s: usize) { - (*B).n += s; -} - -extern "C" { - pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer); - pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: usize) -> *mut c_char; - pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize); - pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char); - pub fn luaL_addvalue(B: *mut luaL_Buffer); - pub fn luaL_pushresult(B: *mut luaL_Buffer); - pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: usize); - pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: usize) -> *mut c_char; -} - -pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char { - luaL_prepbuffsize(B, LUAL_BUFFERSIZE as usize) -} +// TODO: Add buffer API // omitted: old module system compatibility diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index 3c29a04..ae14a6b 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -1,5 +1,6 @@ // The MIT License (MIT) // +// Copyright (c) 2019 A. Orlenko // Copyright (c) 2014 J.C. Moyer // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,20 +23,33 @@ //! Contains definitions from `lua.h`. -use std::os::raw::{c_char, c_int, c_uchar, c_void}; +#[cfg(feature = "lua53")] +use std::os::raw::c_uchar; +use std::os::raw::{c_char, c_int, c_void}; use std::ptr; use super::luaconf; +pub use super::glue::{LUA_RELEASE, LUA_VERSION, LUA_VERSION_NUM}; + pub use super::glue::LUA_REGISTRYINDEX; -pub use super::glue::{LUA_AUTHORS, LUA_COPYRIGHT, LUA_RELEASE, LUA_VERSION}; -pub use super::glue::{LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_NUM, LUA_VERSION_RELEASE}; +#[cfg(not(feature = "lua53"))] +pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX}; pub const LUA_SIGNATURE: &'static [u8] = b"\x1bLua"; // option for multiple returns in 'lua_pcall' and 'lua_call' pub const LUA_MULTRET: c_int = -1; +#[cfg(not(feature = "lua53"))] +pub use super::compat53::{ + lua_absindex, lua_compare, lua_copy, lua_getextraspace, lua_getfield, lua_geti, lua_gettable, + lua_getuservalue, lua_isinteger, lua_len, lua_pushglobaltable, lua_pushlstring, lua_pushstring, + lua_rawget, lua_rawgeti, lua_rawgetp, lua_rawlen, lua_rawsetp, lua_resume, lua_rotate, + lua_seti, lua_setuservalue, lua_tointegerx, lua_tonumberx, lua_upvalueindex, +}; + +#[cfg(feature = "lua53")] #[inline(always)] pub fn lua_upvalueindex(i: c_int) -> c_int { LUA_REGISTRYINDEX - i @@ -47,7 +61,11 @@ pub const LUA_YIELD: c_int = 1; pub const LUA_ERRRUN: c_int = 2; pub const LUA_ERRSYNTAX: c_int = 3; pub const LUA_ERRMEM: c_int = 4; +#[cfg(feature = "lua53")] pub const LUA_ERRGCMM: c_int = 5; +#[cfg(not(feature = "lua53"))] +pub const LUA_ERRERR: c_int = 5; +#[cfg(feature = "lua53")] pub const LUA_ERRERR: c_int = 6; pub type lua_State = c_void; @@ -65,14 +83,18 @@ pub const LUA_TFUNCTION: c_int = 6; pub const LUA_TUSERDATA: c_int = 7; pub const LUA_TTHREAD: c_int = 8; +#[cfg(feature = "lua53")] pub const LUA_NUMTAGS: c_int = 9; // minimum stack available to a C function pub const LUA_MINSTACK: c_int = 20; // predefined values in the registry +#[cfg(feature = "lua53")] pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1; +#[cfg(feature = "lua53")] pub const LUA_RIDX_GLOBALS: lua_Integer = 2; +#[cfg(feature = "lua53")] pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS; /// A Lua number, usually equivalent to `f64`. @@ -82,15 +104,18 @@ pub type lua_Number = luaconf::LUA_NUMBER; pub type lua_Integer = luaconf::LUA_INTEGER; // unsigned integer type +#[cfg(feature = "lua53")] pub type lua_Unsigned = luaconf::LUA_UNSIGNED; // type for continuation-function contexts +#[cfg(feature = "lua53")] pub type lua_KContext = luaconf::LUA_KCONTEXT; /// Type for native functions that can be passed to Lua. pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int; // Type for continuation functions +#[cfg(feature = "lua53")] pub type lua_KFunction = unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int; @@ -116,14 +141,24 @@ extern "C" { pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction; + #[cfg(feature = "lua53")] pub fn lua_version(L: *mut lua_State) -> *const lua_Number; // basic stack manipulation + #[cfg(feature = "lua53")] pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_gettop(L: *mut lua_State) -> c_int; pub fn lua_settop(L: *mut lua_State, idx: c_int); pub fn lua_pushvalue(L: *mut lua_State, idx: c_int); + #[cfg(not(feature = "lua53"))] + pub fn lua_remove(L: *mut lua_State, idx: c_int); + #[cfg(not(feature = "lua53"))] + pub fn lua_insert(L: *mut lua_State, idx: c_int); + #[cfg(not(feature = "lua53"))] + pub fn lua_replace(L: *mut lua_State, idx: c_int); + #[cfg(feature = "lua53")] pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int); + #[cfg(feature = "lua53")] pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int); pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int; @@ -133,15 +168,25 @@ extern "C" { pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int; + #[cfg(feature = "lua53")] pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char; + #[cfg(not(feature = "lua53"))] + pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number; + #[cfg(feature = "lua53")] pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number; + #[cfg(not(feature = "lua53"))] + pub fn lua_tointeger(L: *mut lua_State, idx: c_int) -> lua_Integer; + #[cfg(feature = "lua53")] pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer; pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; + #[cfg(not(feature = "lua53"))] + pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize; + #[cfg(feature = "lua53")] pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize; pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> lua_CFunction; pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void; @@ -150,22 +195,37 @@ extern "C" { } // Comparison and arithmetic functions +#[cfg(feature = "lua53")] pub const LUA_OPADD: c_int = 0; +#[cfg(feature = "lua53")] pub const LUA_OPSUB: c_int = 1; +#[cfg(feature = "lua53")] pub const LUA_OPMUL: c_int = 2; +#[cfg(feature = "lua53")] pub const LUA_OPMOD: c_int = 3; +#[cfg(feature = "lua53")] pub const LUA_OPPOW: c_int = 4; +#[cfg(feature = "lua53")] pub const LUA_OPDIV: c_int = 5; +#[cfg(feature = "lua53")] pub const LUA_OPIDIV: c_int = 6; +#[cfg(feature = "lua53")] pub const LUA_OPBAND: c_int = 7; +#[cfg(feature = "lua53")] pub const LUA_OPBOR: c_int = 8; +#[cfg(feature = "lua53")] pub const LUA_OPBXOR: c_int = 9; +#[cfg(feature = "lua53")] pub const LUA_OPSHL: c_int = 10; +#[cfg(feature = "lua53")] pub const LUA_OPSHR: c_int = 11; +#[cfg(feature = "lua53")] pub const LUA_OPUNM: c_int = 12; +#[cfg(feature = "lua53")] pub const LUA_OPBNOT: c_int = 13; extern "C" { + #[cfg(feature = "lua53")] pub fn lua_arith(L: *mut lua_State, op: c_int); } @@ -174,7 +234,12 @@ pub const LUA_OPLT: c_int = 1; pub const LUA_OPLE: c_int = 2; extern "C" { + #[cfg(not(feature = "lua53"))] + pub fn lua_equal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; + #[cfg(not(feature = "lua53"))] + pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; + #[cfg(feature = "lua53")] pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int; } @@ -183,8 +248,19 @@ extern "C" { pub fn lua_pushnil(L: *mut lua_State); pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); + + #[cfg(feature = "lua53")] pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_pushlstring"] + pub fn lua_pushlstring_51(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char; + + #[cfg(feature = "lua53")] pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_pushstring"] + pub fn lua_pushstring_51(L: *mut lua_State, s: *const c_char) -> *const c_char; + // TODO: omitted: // lua_pushvfstring pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char; @@ -196,35 +272,70 @@ extern "C" { // get functions (Lua -> stack) extern "C" { + #[cfg(feature = "lua53")] pub fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int; + + #[cfg(feature = "lua53")] pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_gettable"] + pub fn lua_gettable_51(L: *mut lua_State, idx: c_int) -> c_int; + + #[cfg(feature = "lua53")] pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_getfield"] + pub fn lua_getfield_51(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int; + + #[cfg(feature = "lua53")] pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int; + + #[cfg(feature = "lua53")] pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_rawget"] + pub fn lua_rawget_51(L: *mut lua_State, idx: c_int); + + #[cfg(feature = "lua53")] pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_rawgeti"] + pub fn lua_rawgeti_51(L: *mut lua_State, idx: c_int, n: lua_Integer); + + #[cfg(feature = "lua53")] pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int; pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int); pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void; pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int; + #[cfg(feature = "lua53")] pub fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int; + #[cfg(not(feature = "lua53"))] + pub fn lua_getfenv(L: *mut lua_State, idx: c_int); } // set functions (stack -> Lua) extern "C" { + #[cfg(feature = "lua53")] pub fn lua_setglobal(L: *mut lua_State, var: *const c_char); pub fn lua_settable(L: *mut lua_State, idx: c_int); pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char); + #[cfg(feature = "lua53")] pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer); pub fn lua_rawset(L: *mut lua_State, idx: c_int); pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer); + #[cfg(feature = "lua53")] pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void); pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int; + #[cfg(feature = "lua53")] pub fn lua_setuservalue(L: *mut lua_State, idx: c_int); + #[cfg(not(feature = "lua53"))] + pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int; } // 'load' and 'call' functions (load and run Lua code) extern "C" { + #[cfg(feature = "lua53")] pub fn lua_callk( L: *mut lua_State, nargs: c_int, @@ -232,6 +343,7 @@ extern "C" { ctx: lua_KContext, k: Option, ); + #[cfg(feature = "lua53")] pub fn lua_pcallk( L: *mut lua_State, nargs: c_int, @@ -240,6 +352,10 @@ extern "C" { ctx: lua_KContext, k: Option, ) -> c_int; + #[cfg(not(feature = "lua53"))] + pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int); + #[cfg(not(feature = "lua53"))] + pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int; pub fn lua_load( L: *mut lua_State, reader: lua_Reader, @@ -247,6 +363,7 @@ extern "C" { chunkname: *const c_char, mode: *const c_char, ) -> c_int; + // FIXME pub fn lua_dump( L: *mut lua_State, writer: lua_Writer, @@ -255,11 +372,13 @@ extern "C" { ) -> c_int; } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) { lua_callk(L, n, r, 0, None) } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int { lua_pcallk(L, n, r, f, 0, None) @@ -267,17 +386,28 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in // coroutine functions extern "C" { + #[cfg(feature = "lua53")] pub fn lua_yieldk( L: *mut lua_State, nresults: c_int, ctx: lua_KContext, k: Option, ) -> c_int; + #[cfg(not(feature = "lua53"))] + pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int; + + #[cfg(feature = "lua53")] pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int; + #[cfg(not(feature = "lua53"))] + #[link_name = "lua_resume"] + pub fn lua_resume_51(L: *mut lua_State, narg: c_int) -> c_int; + pub fn lua_status(L: *mut lua_State) -> c_int; + #[cfg(feature = "lua53")] pub fn lua_isyieldable(L: *mut lua_State) -> c_int; } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int { lua_yieldk(L, n, 0, None) @@ -292,6 +422,7 @@ pub const LUA_GCCOUNTB: c_int = 4; pub const LUA_GCSTEP: c_int = 5; pub const LUA_GCSETPAUSE: c_int = 6; pub const LUA_GCSETSTEPMUL: c_int = 7; +#[cfg(feature = "lua53")] pub const LUA_GCISRUNNING: c_int = 9; extern "C" { @@ -303,7 +434,9 @@ extern "C" { pub fn lua_error(L: *mut lua_State) -> !; pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_concat(L: *mut lua_State, n: c_int); + #[cfg(feature = "lua53")] pub fn lua_len(L: *mut lua_State, idx: c_int); + #[cfg(feature = "lua53")] pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize; pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc; pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void); @@ -311,16 +444,19 @@ extern "C" { // some useful macros // here, implemented as Rust functions +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void { L.offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number { lua_tonumberx(L, i, ptr::null_mut()) } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer { lua_tointegerx(L, i, ptr::null_mut()) @@ -387,7 +523,6 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int { (lua_type(L, n) <= 0) as c_int } -// TODO: Test #[inline(always)] pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_char { use std::ffi::CString; @@ -395,6 +530,19 @@ pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_ch lua_pushlstring(L, c_str.as_ptr(), c_str.as_bytes().len()) } +#[cfg(not(feature = "lua53"))] +#[inline(always)] +pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) { + lua_setfield(L, LUA_GLOBALSINDEX, var) +} + +#[cfg(not(feature = "lua53"))] +#[inline(always)] +pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int { + lua_getfield(L, LUA_GLOBALSINDEX, var) +} + +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int { lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) @@ -405,17 +553,20 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char { lua_tolstring(L, i, ptr::null_mut()) } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) { lua_rotate(L, idx, 1) } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) { lua_rotate(L, idx, -1); lua_pop(L, 1) } +#[cfg(feature = "lua53")] #[inline(always)] pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) { lua_copy(L, -1, idx); @@ -437,7 +588,7 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize); pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize); /// Type for functions to be called on debug events. -pub type lua_Hook = Option; +pub type lua_Hook = extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug); extern "C" { pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int; @@ -447,15 +598,18 @@ extern "C" { pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; + #[cfg(feature = "lua53")] pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void; + #[cfg(feature = "lua53")] pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int); pub fn lua_sethook(L: *mut lua_State, func: lua_Hook, mask: c_int, count: c_int); - pub fn lua_gethook(L: *mut lua_State) -> lua_Hook; + pub fn lua_gethook(L: *mut lua_State) -> Option; pub fn lua_gethookmask(L: *mut lua_State) -> c_int; pub fn lua_gethookcount(L: *mut lua_State) -> c_int; } +#[cfg(feature = "lua53")] #[repr(C)] pub struct lua_Debug { pub event: c_int, @@ -474,3 +628,20 @@ pub struct lua_Debug { // lua.h mentions this is for private use i_ci: *mut c_void, } + +#[cfg(not(feature = "lua53"))] +#[repr(C)] +pub struct lua_Debug { + pub event: c_int, + pub name: *const c_char, + pub namewhat: *const c_char, + pub what: *const c_char, + pub source: *const c_char, + pub currentline: c_int, + pub nups: c_int, + pub linedefined: c_int, + pub lastlinedefined: c_int, + pub short_src: [c_char; luaconf::LUA_IDSIZE as usize], + // lua.h mentions this is for private use + i_ci: c_int, +} diff --git a/src/ffi/luaconf.rs b/src/ffi/luaconf.rs index ad33352..f966b46 100644 --- a/src/ffi/luaconf.rs +++ b/src/ffi/luaconf.rs @@ -1,5 +1,6 @@ // The MIT License (MIT) // +// Copyright (c) 2019 A. Orlenko // Copyright (c) 2014 J.C. Moyer // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,24 +26,13 @@ pub use super::glue::LUAL_BUFFERSIZE; pub use super::glue::LUA_INTEGER; pub use super::glue::LUA_NUMBER; +#[cfg(feature = "lua53")] pub use super::glue::LUA_UNSIGNED; pub use super::glue::LUA_IDSIZE; -pub use super::glue::{LUA_MAXINTEGER, LUA_MININTEGER}; -pub use super::glue::LUAI_MAXSTACK; +#[cfg(feature = "lua53")] pub use super::glue::LUAL_NUMSIZES; +#[cfg(feature = "lua53")] pub type LUA_KCONTEXT = isize; // intptr_t - -use std::os::raw::c_int; - -#[inline(always)] -pub unsafe fn lua_numtointeger(n: LUA_NUMBER, p: *mut LUA_INTEGER) -> c_int { - if n >= (LUA_MININTEGER as LUA_NUMBER) && n < -(LUA_MININTEGER as LUA_NUMBER) { - *p = n as LUA_INTEGER; - 1 - } else { - 0 - } -} diff --git a/src/ffi/lualib.rs b/src/ffi/lualib.rs index 520e499..b3c4998 100644 --- a/src/ffi/lualib.rs +++ b/src/ffi/lualib.rs @@ -1,5 +1,6 @@ // The MIT License (MIT) // +// Copyright (c) 2019 A. Orlenko // Copyright (c) 2014 J.C. Moyer // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,18 +28,24 @@ use std::os::raw::c_int; use super::lua::lua_State; pub use super::glue::{ - LUA_BITLIBNAME, LUA_COLIBNAME, LUA_DBLIBNAME, LUA_IOLIBNAME, LUA_LOADLIBNAME, LUA_MATHLIBNAME, - LUA_OSLIBNAME, LUA_STRLIBNAME, LUA_TABLIBNAME, LUA_UTF8LIBNAME, + LUA_COLIBNAME, LUA_DBLIBNAME, LUA_IOLIBNAME, LUA_LOADLIBNAME, LUA_MATHLIBNAME, LUA_OSLIBNAME, + LUA_STRLIBNAME, LUA_TABLIBNAME, }; +#[cfg(feature = "lua53")] +pub use super::glue::{LUA_BITLIBNAME, LUA_UTF8LIBNAME}; + extern "C" { pub fn luaopen_base(L: *mut lua_State) -> c_int; + #[cfg(feature = "lua53")] pub fn luaopen_coroutine(L: *mut lua_State) -> c_int; pub fn luaopen_table(L: *mut lua_State) -> c_int; pub fn luaopen_io(L: *mut lua_State) -> c_int; pub fn luaopen_os(L: *mut lua_State) -> c_int; pub fn luaopen_string(L: *mut lua_State) -> c_int; + #[cfg(feature = "lua53")] pub fn luaopen_utf8(L: *mut lua_State) -> c_int; + #[cfg(feature = "lua53")] pub fn luaopen_bit32(L: *mut lua_State) -> c_int; pub fn luaopen_math(L: *mut lua_State) -> c_int; pub fn luaopen_debug(L: *mut lua_State) -> c_int; diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 8e41355..14ac3c0 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1,5 +1,6 @@ // The MIT License (MIT) // +// Copyright (c) 2019 A. Orlenko // Copyright (c) 2014 J.C. Moyer // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,22 +30,23 @@ use std::os::raw::c_int; // This is more or less in the order it appears in the Lua manual, with the // exception of constants, which appear scattered throughout the manual text. -// luaconf.h functions -pub use self::luaconf::lua_numtointeger; - // C API types pub use self::lua::{ - lua_Alloc, lua_CFunction, lua_Debug, lua_Hook, lua_Integer, lua_KContext, lua_KFunction, - lua_Number, lua_Reader, lua_State, lua_Unsigned, lua_Writer, + lua_Alloc, lua_CFunction, lua_Debug, lua_Hook, lua_Integer, lua_Number, lua_Reader, lua_State, + lua_Writer, }; +#[cfg(feature = "lua53")] +pub use self::lua::{lua_KContext, lua_KFunction, lua_Unsigned}; + +#[cfg(not(feature = "lua53"))] +pub use self::lua::lua_setfenv; + // C API functions pub use self::lua::{ lua_absindex, - lua_arith, lua_atpanic, lua_call, - lua_callk, lua_checkstack, lua_close, lua_compare, @@ -84,7 +86,6 @@ pub use self::lua::{ lua_istable, lua_isthread, lua_isuserdata, - lua_isyieldable, lua_len, lua_load, lua_newstate, @@ -93,7 +94,6 @@ pub use self::lua::{ lua_newuserdata, lua_next, lua_pcall, - lua_pcallk, lua_pop, lua_pushboolean, lua_pushcclosure, @@ -135,7 +135,6 @@ pub use self::lua::{ lua_setupvalue, lua_setuservalue, lua_status, - lua_stringtonumber, lua_toboolean, lua_tocfunction, lua_tointeger, @@ -149,61 +148,76 @@ pub use self::lua::{ lua_touserdata, lua_type, lua_typename, - lua_upvalueid, lua_upvalueindex, - lua_upvaluejoin, - lua_version, lua_xmove, lua_yield, - lua_yieldk, +}; + +#[cfg(feature = "lua53")] +pub use self::lua::{ + lua_arith, lua_callk, lua_isyieldable, lua_pcallk, lua_stringtonumber, lua_upvalueid, + lua_upvaluejoin, lua_version, lua_yieldk, }; // auxiliary library types -pub use self::lauxlib::{luaL_Buffer, luaL_Reg}; +pub use self::lauxlib::luaL_Reg; // auxiliary library functions pub use self::lauxlib::{ - luaL_addchar, luaL_addlstring, luaL_addsize, luaL_addstring, luaL_addvalue, luaL_argcheck, - luaL_argerror, luaL_buffinit, luaL_buffinitsize, luaL_callmeta, luaL_checkany, luaL_checkint, - luaL_checkinteger, luaL_checklong, luaL_checklstring, luaL_checknumber, luaL_checkoption, - luaL_checkstack, luaL_checkstring, luaL_checktype, luaL_checkudata, luaL_checkversion, - luaL_dofile, luaL_dostring, luaL_error, luaL_execresult, luaL_fileresult, luaL_getmetafield, - luaL_getmetatable, luaL_getsubtable, luaL_gsub, luaL_len, luaL_loadbuffer, luaL_loadbufferx, - luaL_loadfile, luaL_loadfilex, luaL_loadstring, luaL_newlib, luaL_newlibtable, + luaL_argcheck, luaL_argerror, luaL_callmeta, luaL_checkany, luaL_checkint, luaL_checkinteger, + luaL_checklong, luaL_checklstring, luaL_checknumber, luaL_checkoption, luaL_checkstack, + luaL_checkstring, luaL_checktype, luaL_checkudata, luaL_dofile, luaL_dostring, luaL_error, + luaL_getmetafield, luaL_getmetatable, luaL_getsubtable, luaL_gsub, luaL_len, luaL_loadbuffer, + luaL_loadbufferx, luaL_loadfile, luaL_loadstring, luaL_newlib, luaL_newlibtable, luaL_newmetatable, luaL_newstate, luaL_optint, luaL_optinteger, luaL_optlong, luaL_optlstring, - luaL_optnumber, luaL_optstring, luaL_prepbuffer, luaL_prepbuffsize, luaL_pushresult, - luaL_pushresultsize, luaL_ref, luaL_requiref, luaL_setfuncs, luaL_setmetatable, luaL_testudata, - luaL_tolstring, luaL_traceback, luaL_typename, luaL_unref, luaL_where, + luaL_optnumber, luaL_optstring, luaL_ref, luaL_requiref, luaL_setfuncs, luaL_setmetatable, + luaL_testudata, luaL_tolstring, luaL_traceback, luaL_typename, luaL_unref, luaL_where, }; +#[cfg(feature = "lua53")] +pub use self::lauxlib::{luaL_checkversion, luaL_execresult, luaL_fileresult, luaL_loadfilex}; + // lualib.h functions pub use self::lualib::{ - luaL_openlibs, luaopen_base, luaopen_bit32, luaopen_coroutine, luaopen_debug, luaopen_io, - luaopen_math, luaopen_os, luaopen_package, luaopen_string, luaopen_table, luaopen_utf8, + luaL_openlibs, luaopen_base, luaopen_debug, luaopen_io, luaopen_math, luaopen_os, + luaopen_package, luaopen_string, luaopen_table, }; +#[cfg(feature = "lua53")] +pub use self::lualib::{luaopen_bit32, luaopen_coroutine, luaopen_utf8}; + // constants from lua.h pub use self::lua::{ - LUA_ERRERR, LUA_ERRGCMM, LUA_ERRMEM, LUA_ERRRUN, LUA_ERRSYNTAX, LUA_GCCOLLECT, LUA_GCCOUNT, - LUA_GCCOUNTB, LUA_GCISRUNNING, LUA_GCRESTART, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCSTEP, - LUA_GCSTOP, LUA_HOOKCALL, LUA_HOOKCOUNT, LUA_HOOKLINE, LUA_HOOKRET, LUA_HOOKTAILCALL, - LUA_MASKCALL, LUA_MASKCOUNT, LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, - LUA_OPADD, LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, LUA_OPDIV, LUA_OPEQ, LUA_OPIDIV, - LUA_OPLE, LUA_OPLT, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSHL, LUA_OPSHR, LUA_OPSUB, - LUA_OPUNM, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD, LUA_TBOOLEAN, - LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, - LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD, + LUA_ERRERR, LUA_ERRMEM, LUA_ERRRUN, LUA_ERRSYNTAX, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCCOUNTB, + LUA_GCRESTART, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCSTEP, LUA_GCSTOP, LUA_HOOKCALL, + LUA_HOOKCOUNT, LUA_HOOKLINE, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_MASKCALL, LUA_MASKCOUNT, + LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, LUA_OPEQ, LUA_OPLE, LUA_OPLT, + LUA_REGISTRYINDEX, LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE, + LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD, }; +#[cfg(feature = "lua53")] +pub use self::lua::{ + LUA_ERRGCMM, LUA_GCISRUNNING, LUA_OPADD, LUA_OPBAND, LUA_OPBNOT, LUA_OPBOR, LUA_OPBXOR, + LUA_OPDIV, LUA_OPIDIV, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSHL, LUA_OPSHR, LUA_OPSUB, + LUA_OPUNM, LUA_RIDX_GLOBALS, LUA_RIDX_MAINTHREAD, +}; + +#[cfg(not(feature = "lua53"))] +pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX}; + // constants from lauxlib.h -pub use self::lauxlib::{LUA_ERRFILE, LUA_FILEHANDLE, LUA_NOREF, LUA_REFNIL}; +pub use self::lauxlib::{LUA_ERRFILE, LUA_NOREF, LUA_REFNIL}; // constants from lualib.h pub use self::lualib::{ - LUA_BITLIBNAME, LUA_COLIBNAME, LUA_DBLIBNAME, LUA_IOLIBNAME, LUA_LOADLIBNAME, LUA_MATHLIBNAME, - LUA_OSLIBNAME, LUA_STRLIBNAME, LUA_TABLIBNAME, LUA_UTF8LIBNAME, + LUA_COLIBNAME, LUA_DBLIBNAME, LUA_IOLIBNAME, LUA_LOADLIBNAME, LUA_MATHLIBNAME, LUA_OSLIBNAME, + LUA_STRLIBNAME, LUA_TABLIBNAME, }; +#[cfg(feature = "lua53")] +pub use self::lualib::{LUA_BITLIBNAME, LUA_UTF8LIBNAME}; + // Not actually defined in lua.h / luaconf.h pub const LUA_MAX_UPVALUES: c_int = 255; @@ -212,6 +226,9 @@ mod glue { include!(concat!(env!("OUT_DIR"), "/glue.rs")); } +#[cfg(not(feature = "lua53"))] +mod compat53; + mod lauxlib; mod lua; mod luaconf; diff --git a/src/lua.rs b/src/lua.rs index d0c1c8c..73e9011 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -16,6 +16,8 @@ use crate::table::Table; use crate::thread::Thread; use crate::types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey}; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; +#[cfg(not(feature = "lua53"))] +use crate::util::set_main_state; use crate::util::{ assert_stack, callback_error, check_stack, get_main_state, get_userdata, get_wrapped_error, init_error_registry, init_userdata_metatable, pop_error, protect_lua, protect_lua_closure, @@ -34,9 +36,41 @@ pub struct Lua { unsafe impl Send for Lua {} impl Lua { + // Creates a new Lua state and loads standard library without the debug library. + #[doc(hidden)] + pub fn new() -> Lua { + unsafe { + let state = ffi::luaL_newstate(); + + ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); + #[cfg(feature = "lua53")] + ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1); + ffi::luaL_requiref(state, cstr!("table"), ffi::luaopen_table, 1); + ffi::luaL_requiref(state, cstr!("io"), ffi::luaopen_io, 1); + ffi::luaL_requiref(state, cstr!("os"), ffi::luaopen_os, 1); + ffi::luaL_requiref(state, cstr!("string"), ffi::luaopen_string, 1); + #[cfg(feature = "lua53")] + ffi::luaL_requiref(state, cstr!("utf8"), ffi::luaopen_utf8, 1); + ffi::luaL_requiref(state, cstr!("math"), ffi::luaopen_math, 1); + ffi::luaL_requiref(state, cstr!("package"), ffi::luaopen_package, 1); + #[cfg(feature = "lua53")] + ffi::lua_pop(state, 9); + #[cfg(not(feature = "lua53"))] + ffi::lua_pop(state, 7); + + Lua::init_from_ptr(state) + } + } + /// Constructs a new Lua instance from the existing state. pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua { + #[cfg(feature = "lua53")] let main_state = get_main_state(state); + #[cfg(not(feature = "lua53"))] + let main_state = { + set_main_state(state); + state + }; let main_state_top = ffi::lua_gettop(state); let ref_thread = mlua_expect!( @@ -113,6 +147,7 @@ impl Lua { } /// Returns true if the garbage collector is currently running automatically. + #[cfg(feature = "lua53")] pub fn gc_is_running(&self) -> bool { unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCISRUNNING, 0) != 0 } } @@ -231,7 +266,10 @@ impl Lua { ffi::LUA_OK => { if let Some(env) = env { self.push_value(env)?; + #[cfg(feature = "lua53")] ffi::lua_setupvalue(self.state, -2, 1); + #[cfg(not(feature = "lua53"))] + ffi::lua_setfenv(self.state, -2); } Ok(Function(self.pop_ref())) } @@ -421,7 +459,10 @@ impl Lua { unsafe { let _sg = StackGuard::new(self.state); assert_stack(self.state, 2); + #[cfg(feature = "lua53")] ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); + #[cfg(not(feature = "lua53"))] + ffi::lua_pushvalue(self.state, ffi::LUA_GLOBALSINDEX); Table(self.pop_ref()) } } diff --git a/src/scope.rs b/src/scope.rs index 238e52d..2c88083 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -175,6 +175,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { assert_stack(lua.state, 1); lua.push_ref(&u.0); ffi::lua_getuservalue(lua.state, -1); + #[cfg(not(feature = "lua53"))] + { + ffi::lua_pushinteger(lua.state, 1); + ffi::lua_gettable(lua.state, -2); + ffi::lua_remove(lua.state, -2); + } return ffi::lua_touserdata(lua.state, -1) == check_data.as_ptr() as *mut c_void; } @@ -239,7 +245,16 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { assert_stack(lua.state, 6); push_userdata(lua.state, ())?; + #[cfg(feature = "lua53")] ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void); + #[cfg(not(feature = "lua53"))] + protect_lua_closure(lua.state, 0, 1, |state| { + // Lua 5.1 allows to store only table. Then we will wrap the value. + ffi::lua_createtable(state, 1, 0); + ffi::lua_pushinteger(state, 1); + ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void); + ffi::lua_settable(state, -3); + })?; ffi::lua_setuservalue(lua.state, -2); protect_lua_closure(lua.state, 0, 1, move |state| { diff --git a/src/userdata.rs b/src/userdata.rs index 5a52165..617e0c1 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -335,6 +335,14 @@ impl<'lua> AnyUserData<'lua> { /// [`get_user_value`]: #method.get_user_value pub fn set_user_value>(&self, v: V) -> Result<()> { let lua = self.0.lua; + #[cfg(not(feature = "lua53"))] + let v = { + // Lua 5.1 allows to store only table. Then we will wrap the value. + let t = lua.create_table()?; + t.raw_set(1, v)?; + crate::Value::Table(t) + }; + #[cfg(feature = "lua53")] let v = v.to_lua(lua)?; unsafe { let _sg = StackGuard::new(lua.state); @@ -358,6 +366,9 @@ impl<'lua> AnyUserData<'lua> { ffi::lua_getuservalue(lua.state, -1); lua.pop_value() }; + #[cfg(not(feature = "lua53"))] + return crate::Table::from_lua(res, lua)?.get(1); + #[cfg(feature = "lua53")] V::from_lua(res, lua) } diff --git a/src/util.rs b/src/util.rs index acae9ae..513d35f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -192,7 +192,8 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { Error::SyntaxError { // This seems terrible, but as far as I can tell, this is exactly what the // stock Lua REPL does. - incomplete_input: err_string.ends_with(""), + incomplete_input: err_string.ends_with("") + || err_string.ends_with("''"), message: err_string, } } @@ -204,6 +205,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { Error::RuntimeError(err_string) } ffi::LUA_ERRMEM => Error::MemoryError(err_string), + #[cfg(feature = "lua53")] ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string), _ => mlua_panic!("unrecognized lua error code"), } @@ -441,9 +443,23 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { 1 } +// Does not call lua_checkstack, uses 2 stack spaces. +#[cfg(not(feature = "lua53"))] +pub unsafe fn set_main_state(state: *mut ffi::lua_State) { + ffi::lua_pushlightuserdata(state, &MAIN_THREAD_REGISTRY_KEY as *const u8 as *mut c_void); + ffi::lua_pushthread(state); + ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); +} + // Does not call lua_checkstack, uses 1 stack space. pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State { + #[cfg(feature = "lua53")] ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD); + #[cfg(not(feature = "lua53"))] + { + ffi::lua_pushlightuserdata(state, &MAIN_THREAD_REGISTRY_KEY as *const u8 as *mut c_void); + ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); + } let main_state = ffi::lua_tothread(state, -1); ffi::lua_pop(state, 1); main_state @@ -735,6 +751,8 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) { ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); } +#[cfg(not(feature = "lua53"))] +static MAIN_THREAD_REGISTRY_KEY: u8 = 0; static ERROR_METATABLE_REGISTRY_KEY: u8 = 0; static PANIC_METATABLE_REGISTRY_KEY: u8 = 0; static DESTRUCTED_USERDATA_METATABLE: u8 = 0; diff --git a/tests/_lua.rs b/tests/_lua.rs deleted file mode 100644 index 4d49a76..0000000 --- a/tests/_lua.rs +++ /dev/null @@ -1,53 +0,0 @@ -#[allow(non_camel_case_types)] -type lua_State = std::os::raw::c_void; - -#[allow(non_camel_case_types)] -type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> std::os::raw::c_int; - -extern "C" { - fn luaL_newstate() -> *mut lua_State; - - fn luaL_requiref( - L: *mut lua_State, - modname: *const std::os::raw::c_char, - openf: lua_CFunction, - glb: std::os::raw::c_int, - ); - - fn lua_settop(L: *mut lua_State, idx: std::os::raw::c_int); - - fn luaopen_base(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_coroutine(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_table(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_io(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_os(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_string(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_math(L: *mut lua_State) -> std::os::raw::c_int; - fn luaopen_package(L: *mut lua_State) -> std::os::raw::c_int; -} - -#[allow(unused)] -fn make_lua() -> mlua::Lua { - macro_rules! cstr { - ($s:expr) => { - concat!($s, "\0") as *const str as *const ::std::os::raw::c_char - }; - } - - unsafe { - let state = luaL_newstate(); - - // Do not open the debug library, it can be used to cause unsafety. - luaL_requiref(state, cstr!("_G"), luaopen_base, 1); - luaL_requiref(state, cstr!("coroutine"), luaopen_coroutine, 1); - luaL_requiref(state, cstr!("table"), luaopen_table, 1); - luaL_requiref(state, cstr!("io"), luaopen_io, 1); - luaL_requiref(state, cstr!("os"), luaopen_os, 1); - luaL_requiref(state, cstr!("string"), luaopen_string, 1); - luaL_requiref(state, cstr!("math"), luaopen_math, 1); - luaL_requiref(state, cstr!("package"), luaopen_package, 1); - lua_settop(state, -8 - 1); - - mlua::Lua::init_from_ptr(state) - } -} diff --git a/tests/byte_string.rs b/tests/byte_string.rs index 6133d6f..3b05ee5 100644 --- a/tests/byte_string.rs +++ b/tests/byte_string.rs @@ -1,21 +1,19 @@ use bstr::{BStr, BString}; -use mlua::Result; - -include!("_lua.rs"); +use mlua::{Lua, Result}; #[test] fn byte_string_round_trip() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.load( r#" - invalid_sequence_identifier = "\xa0\xa1" - invalid_2_octet_sequence_2nd = "\xc3\x28" - invalid_3_octet_sequence_2nd = "\xe2\x28\xa1" - invalid_3_octet_sequence_3rd = "\xe2\x82\x28" - invalid_4_octet_sequence_2nd = "\xf0\x28\x8c\xbc" - invalid_4_octet_sequence_3rd = "\xf0\x90\x28\xbc" - invalid_4_octet_sequence_4th = "\xf0\x28\x8c\x28" + invalid_sequence_identifier = "\160\161" + invalid_2_octet_sequence_2nd = "\195\040" + invalid_3_octet_sequence_2nd = "\226\040\161" + invalid_3_octet_sequence_3rd = "\226\130\040" + invalid_4_octet_sequence_2nd = "\240\040\140\188" + invalid_4_octet_sequence_3rd = "\240\144\040\188" + invalid_4_octet_sequence_4th = "\240\040\140\040" an_actual_string = "Hello, world!" "#, diff --git a/tests/function.rs b/tests/function.rs index fb4eea8..cc1c9ab 100644 --- a/tests/function.rs +++ b/tests/function.rs @@ -1,10 +1,8 @@ -use mlua::{Function, Result, String}; - -include!("_lua.rs"); +use mlua::{Function, Lua, Result, String}; #[test] fn test_function() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -24,7 +22,7 @@ fn test_function() -> Result<()> { #[test] fn test_bind() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -54,7 +52,7 @@ fn test_bind() -> Result<()> { #[test] fn test_rust_function() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( diff --git a/tests/memory.rs b/tests/memory.rs index e74474b..a06ac5f 100644 --- a/tests/memory.rs +++ b/tests/memory.rs @@ -1,18 +1,19 @@ use std::sync::Arc; -use mlua::{Error, Result, UserData}; - -include!("_lua.rs"); +use mlua::{Lua, Result, UserData}; #[test] fn test_gc_control() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); - assert!(lua.gc_is_running()); - lua.gc_stop(); - assert!(!lua.gc_is_running()); - lua.gc_restart(); - assert!(lua.gc_is_running()); + #[cfg(feature = "lua53")] + { + assert!(lua.gc_is_running()); + lua.gc_stop(); + assert!(!lua.gc_is_running()); + lua.gc_restart(); + assert!(lua.gc_is_running()); + } struct MyUserdata(Arc<()>); impl UserData for MyUserdata {} @@ -30,9 +31,12 @@ fn test_gc_control() -> Result<()> { Ok(()) } +#[cfg(feature = "lua53")] #[test] fn test_gc_error() { - let lua = make_lua(); + use mlua::Error; + + let lua = Lua::new(); match lua .load( r#" diff --git a/tests/scope.rs b/tests/scope.rs index 77ff9df..54840d6 100644 --- a/tests/scope.rs +++ b/tests/scope.rs @@ -1,13 +1,11 @@ use std::cell::Cell; use std::rc::Rc; -use mlua::{Error, Function, MetaMethod, Result, String, UserData, UserDataMethods}; - -include!("_lua.rs"); +use mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods}; #[test] fn scope_func() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let rc = Rc::new(Cell::new(0)); lua.scope(|scope| { @@ -34,7 +32,7 @@ fn scope_func() -> Result<()> { #[test] fn scope_drop() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); struct MyUserdata(Rc<()>); impl UserData for MyUserdata { @@ -65,7 +63,7 @@ fn scope_drop() -> Result<()> { #[test] fn scope_capture() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let mut i = 0; lua.scope(|scope| { @@ -83,7 +81,7 @@ fn scope_capture() -> Result<()> { #[test] fn outer_lua_access() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let table = lua.create_table()?; lua.scope(|scope| { @@ -114,7 +112,7 @@ fn scope_userdata_methods() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); let i = Cell::new(42); let f: Function = lua @@ -156,7 +154,7 @@ fn scope_userdata_functions() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); let dummy = 0; let f = lua @@ -192,7 +190,7 @@ fn scope_userdata_mismatch() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); lua.load( r#" diff --git a/tests/string.rs b/tests/string.rs index 1f56094..abe8aaa 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -1,13 +1,11 @@ use std::borrow::Cow; -use mlua::{Result, String}; - -include!("_lua.rs"); +use mlua::{Lua, Result, String}; #[test] fn compare() { fn with_str(s: &str, f: F) { - f(make_lua().create_string(s).unwrap()); + f(Lua::new().create_string(s).unwrap()); } // Tests that all comparisons we want to have are usable @@ -24,12 +22,12 @@ fn compare() { #[test] fn string_views() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.load( r#" ok = "null bytes are valid utf-8, wh\0 knew?" - err = "but \xff isn't :(" + err = "but \255 isn't :(" empty = "" "#, ) @@ -58,7 +56,7 @@ fn string_views() -> Result<()> { #[test] fn raw_string() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let rs = lua.create_string(&[0, 1, 2, 3, 0, 1, 2, 3])?; assert_eq!(rs.as_bytes(), &[0, 1, 2, 3, 0, 1, 2, 3]); diff --git a/tests/table.rs b/tests/table.rs index 0d7593c..1fa20cc 100644 --- a/tests/table.rs +++ b/tests/table.rs @@ -1,10 +1,8 @@ -use mlua::{Nil, Result, Table, Value}; - -include!("_lua.rs"); +use mlua::{Lua, Nil, Result, Table, Value}; #[test] fn test_set_get() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); globals.set("foo", "bar")?; @@ -17,7 +15,7 @@ fn test_set_get() -> Result<()> { #[test] fn test_table() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); @@ -91,7 +89,7 @@ fn test_table() -> Result<()> { #[test] fn test_table_scope() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -119,7 +117,7 @@ fn test_table_scope() -> Result<()> { #[test] fn test_metatable() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let table = lua.create_table()?; let metatable = lua.create_table()?; @@ -141,7 +139,7 @@ fn test_metatable() -> Result<()> { #[test] fn test_table_error() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( diff --git a/tests/tests.rs b/tests/tests.rs index 3a34285..1c850c8 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -3,13 +3,13 @@ use std::panic::catch_unwind; use std::sync::Arc; use std::{error, f32, f64, fmt}; -use mlua::{Error, ExternalError, Function, Nil, Result, String, Table, UserData, Value, Variadic}; - -include!("_lua.rs"); +use mlua::{ + Error, ExternalError, Function, Lua, Nil, Result, String, Table, UserData, Value, Variadic, +}; #[test] fn test_load() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let func = lua.load("return 1+2").into_function()?; let result: i32 = func.call(())?; assert_eq!(result, 3); @@ -21,7 +21,7 @@ fn test_load() -> Result<()> { #[test] fn test_exec() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -56,7 +56,7 @@ fn test_exec() -> Result<()> { #[test] fn test_eval() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); assert_eq!(lua.load("1 + 1").eval::()?, 2); assert_eq!(lua.load("false == false").eval::()?, true); @@ -77,7 +77,7 @@ fn test_eval() -> Result<()> { #[test] fn test_lua_multi() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.load( r#" @@ -108,7 +108,7 @@ fn test_lua_multi() -> Result<()> { #[test] fn test_coercion() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.load( r#" @@ -148,7 +148,7 @@ fn test_error() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -183,6 +183,13 @@ fn test_error() -> Result<()> { end, 3) local function handler(err) + if string.match(_VERSION, ' 5%.1$') then + -- Special case for Lua 5.1 + local caps = string.match(err, ': (%d+)$') + if caps then + err = caps + end + end testvar = testvar + err return "should be ignored" end @@ -260,7 +267,7 @@ fn test_error() -> Result<()> { assert!(understand_recursion.call::<_, ()>(()).is_err()); match catch_unwind(|| -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -288,7 +295,7 @@ fn test_error() -> Result<()> { }; match catch_unwind(|| -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( @@ -321,7 +328,7 @@ fn test_error() -> Result<()> { #[test] fn test_result_conversions() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); let err = lua.create_function(|_, ()| { @@ -352,7 +359,7 @@ fn test_result_conversions() -> Result<()> { #[test] fn test_num_conversion() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); assert_eq!( lua.coerce_integer(Value::String(lua.create_string("1")?))?, @@ -382,7 +389,10 @@ fn test_num_conversion() -> Result<()> { assert_eq!(lua.load("1.0").eval::()?, 1); assert_eq!(lua.load("1.0").eval::()?, 1.0); + #[cfg(feature = "lua53")] assert_eq!(lua.load("1.0").eval::()?, "1.0"); + #[cfg(not(feature = "lua53"))] + assert_eq!(lua.load("1.0").eval::()?, "1"); assert_eq!(lua.load("1.5").eval::()?, 1); assert_eq!(lua.load("1.5").eval::()?, 1.5); @@ -404,7 +414,7 @@ fn test_num_conversion() -> Result<()> { #[test] fn test_pcall_xpcall() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); // make sure that we handle not enough arguments @@ -413,6 +423,18 @@ fn test_pcall_xpcall() -> Result<()> { assert!(lua.load("xpcall()").exec().is_err()); assert!(lua.load("xpcall(function() end)").exec().is_err()); + // Lua5.3 compatible version of xpcall + #[cfg(not(feature = "lua53"))] + lua.load( + r#" + local xpcall_orig = xpcall + function xpcall(f, err, ...) + return xpcall_orig(function() return f(unpack(arg)) end, err) + end + "#, + ) + .exec()?; + // Make sure that the return values from are correct on success let (r, e) = lua @@ -444,7 +466,13 @@ fn test_pcall_xpcall() -> Result<()> { assert_eq!(globals.get::<_, String>("pcall_error")?, "testerror"); assert_eq!(globals.get::<_, bool>("xpcall_statusr")?, false); + #[cfg(feature = "lua53")] assert_eq!(globals.get::<_, String>("xpcall_error")?, "testerror"); + #[cfg(not(feature = "lua53"))] + assert!(globals + .get::<_, String>("xpcall_error")? + .to_str()? + .ends_with(": testerror")); // Make sure that weird xpcall error recursion at least doesn't cause unsafety or panics. lua.load( @@ -464,7 +492,7 @@ fn test_pcall_xpcall() -> Result<()> { #[test] fn test_recursive_mut_callback_error() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let mut v = Some(Box::new(123)); let f = lua.create_function_mut::<_, (), _>(move |lua, mutate: bool| { @@ -499,7 +527,7 @@ fn test_recursive_mut_callback_error() -> Result<()> { #[test] fn test_set_metatable_nil() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.load( r#" a = {} @@ -512,7 +540,7 @@ fn test_set_metatable_nil() -> Result<()> { #[test] fn test_named_registry_value() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.set_named_registry_value::<_, i32>("test", 42)?; let f = lua.create_function(move |lua, ()| { @@ -533,7 +561,7 @@ fn test_named_registry_value() -> Result<()> { #[test] fn test_registry_value() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let mut r = Some(lua.create_registry_value::(42)?); let f = lua.create_function_mut(move |lua, ()| { @@ -557,7 +585,7 @@ fn test_drop_registry_value() -> Result<()> { impl UserData for MyUserdata {} - let lua = make_lua(); + let lua = Lua::new(); let rc = Arc::new(()); let r = lua.create_registry_value(MyUserdata(rc.clone()))?; @@ -575,8 +603,8 @@ fn test_drop_registry_value() -> Result<()> { #[test] fn test_lua_registry_ownership() -> Result<()> { - let lua1 = make_lua(); - let lua2 = make_lua(); + let lua1 = Lua::new(); + let lua2 = Lua::new(); let r1 = lua1.create_registry_value("hello")?; let r2 = lua2.create_registry_value("hello")?; @@ -591,8 +619,8 @@ fn test_lua_registry_ownership() -> Result<()> { #[test] fn test_mismatched_registry_key() -> Result<()> { - let lua1 = make_lua(); - let lua2 = make_lua(); + let lua1 = Lua::new(); + let lua2 = Lua::new(); let r = lua1.create_registry_value("hello")?; match lua2.remove_registry_value(r) { @@ -605,7 +633,7 @@ fn test_mismatched_registry_key() -> Result<()> { #[test] fn too_many_returns() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?; assert!(f.call::<_, Vec>(()).is_err()); Ok(()) @@ -613,7 +641,7 @@ fn too_many_returns() -> Result<()> { #[test] fn too_many_arguments() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); lua.load("function test(...) end").exec()?; let args = Variadic::from_iter(1..1000000); assert!(lua @@ -626,7 +654,7 @@ fn too_many_arguments() -> Result<()> { #[test] fn too_many_recursions() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let f = lua .create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?; lua.globals().set("f", f)?; @@ -642,7 +670,7 @@ fn too_many_recursions() -> Result<()> { #[test] fn too_many_binds() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( r#" @@ -663,7 +691,7 @@ fn too_many_binds() -> Result<()> { #[test] fn large_args() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); globals.set( @@ -698,7 +726,7 @@ fn large_args() -> Result<()> { #[test] fn large_args_ref() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let f = lua.create_function(|_, args: Variadic| { for i in 0..args.len() { @@ -714,7 +742,7 @@ fn large_args_ref() -> Result<()> { #[test] fn chunk_env() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let assert: Function = lua.globals().get("assert")?; @@ -756,7 +784,7 @@ fn chunk_env() -> Result<()> { #[test] fn context_thread() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let f = lua .load( @@ -767,7 +795,32 @@ fn context_thread() -> Result<()> { ) .into_function()?; + #[cfg(feature = "lua53")] f.call::<_, ()>(lua.current_thread())?; + #[cfg(not(feature = "lua53"))] + f.call::<_, ()>(Nil)?; + + Ok(()) +} + +#[test] +#[cfg(not(feature = "lua53"))] +fn context_thread_51() -> Result<()> { + let lua = Lua::new(); + + let thread = lua.create_thread( + lua.load( + r#" + function (thread) + assert(coroutine.running() == thread) + end + "#, + ) + .eval()?, + )?; + + thread.resume::<_, ()>(thread.clone())?; + Ok(()) } diff --git a/tests/thread.rs b/tests/thread.rs index 6042a5a..211e4eb 100644 --- a/tests/thread.rs +++ b/tests/thread.rs @@ -1,12 +1,10 @@ use std::panic::catch_unwind; -use mlua::{Error, Function, Result, Thread, ThreadStatus}; - -include!("_lua.rs"); +use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus}; #[test] fn test_thread() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let thread = lua.create_thread( lua.load( @@ -97,11 +95,18 @@ fn test_thread() -> Result<()> { #[test] fn coroutine_from_closure() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let thrd_main = lua.create_function(|_, ()| Ok(()))?; lua.globals().set("main", thrd_main)?; + + #[cfg(feature = "lua53")] let thrd: Thread = lua.load("coroutine.create(main)").eval()?; + #[cfg(not(feature = "lua53"))] + let thrd: Thread = lua + .load("coroutine.create(function(...) return main(unpack(arg)) end)") + .eval()?; + thrd.resume::<_, ()>(())?; Ok(()) @@ -111,7 +116,7 @@ fn coroutine_from_closure() -> Result<()> { fn coroutine_panic() { match catch_unwind(|| -> Result<()> { // check that coroutines propagate panics correctly - let lua = make_lua(); + let lua = Lua::new(); let thrd_main = lua.create_function(|_, ()| -> Result<()> { panic!("test_panic"); })?; diff --git a/tests/types.rs b/tests/types.rs index 7cd4929..72f9484 100644 --- a/tests/types.rs +++ b/tests/types.rs @@ -1,12 +1,10 @@ use std::os::raw::c_void; -use mlua::{Function, LightUserData, Result}; - -include!("_lua.rs"); +use mlua::{Function, LightUserData, Lua, Result}; #[test] fn test_lightuserdata() -> Result<()> { - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); lua.load( diff --git a/tests/userdata.rs b/tests/userdata.rs index 7730d0d..d521b35 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -1,11 +1,10 @@ use std::sync::Arc; use mlua::{ - AnyUserData, ExternalError, Function, MetaMethod, Result, String, UserData, UserDataMethods, + AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData, + UserDataMethods, }; -include!("_lua.rs"); - #[test] fn test_user_data() -> Result<()> { struct UserData1(i64); @@ -14,7 +13,7 @@ fn test_user_data() -> Result<()> { impl UserData for UserData1 {}; impl UserData for UserData2 {}; - let lua = make_lua(); + let lua = Lua::new(); let userdata1 = lua.create_userdata(UserData1(1))?; let userdata2 = lua.create_userdata(UserData2(Box::new(2)))?; @@ -43,7 +42,7 @@ fn test_methods() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); let userdata = lua.create_userdata(MyUserData(42))?; globals.set("userdata", userdata.clone())?; @@ -96,7 +95,7 @@ fn test_metamethods() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); globals.set("userdata1", MyUserData(7))?; globals.set("userdata2", MyUserData(3))?; @@ -127,7 +126,7 @@ fn test_gc_userdata() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); lua.globals().set("userdata", MyUserdata { id: 123 })?; assert!(lua @@ -160,7 +159,7 @@ fn detroys_userdata() -> Result<()> { let rc = Arc::new(()); - let lua = make_lua(); + let lua = Lua::new(); lua.globals().set("userdata", MyUserdata(rc.clone()))?; assert_eq!(Arc::strong_count(&rc), 2); @@ -179,7 +178,7 @@ fn user_value() -> Result<()> { struct MyUserData; impl UserData for MyUserData {} - let lua = make_lua(); + let lua = Lua::new(); let ud = lua.create_userdata(MyUserData)?; ud.set_user_value("hello")?; assert_eq!(ud.get_user_value::()?, "hello"); @@ -205,7 +204,7 @@ fn test_functions() -> Result<()> { } } - let lua = make_lua(); + let lua = Lua::new(); let globals = lua.globals(); let userdata = lua.create_userdata(MyUserData(42))?; globals.set("userdata", userdata.clone())?;