Turn more comments into doc comments, improve debuggability

This commit is contained in:
Michael Pfaff 2023-07-16 14:39:35 -04:00
parent 58e5c7b18b
commit 061f84f702
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
23 changed files with 898 additions and 159 deletions

View File

@ -39,4 +39,5 @@ cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.10", optional = true }
#luau0-src = { version = "0.5.10", optional = true }
luau0-src = { path = "../../luau-src-rs", optional = true }

View File

@ -7,16 +7,16 @@
use std::os::raw::c_int;
#[cfg(any(feature = "lua54", doc))]
#[cfg(any(feature = "lua54"))]
pub use lua54::*;
#[cfg(any(feature = "lua53", doc))]
#[cfg(any(feature = "lua53"))]
pub use lua53::*;
#[cfg(any(feature = "lua52", doc))]
#[cfg(any(feature = "lua52"))]
pub use lua52::*;
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use lua51::*;
#[cfg(any(feature = "luau", doc))]

View File

@ -30,6 +30,9 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
if level == 0 || lua_istable(L, -1) == 0 {
return 0; // not found
}
if lua_checkstack(L, 3) == 0 {
return 0;
}
lua_pushnil(L); // start 'next' loop
while lua_next(L, -2) != 0 {
@ -42,9 +45,8 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
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_replace(L, -2); // remove table (but keep name), place '.' between the two names
lua_concat(L, 3);
return 1;
}
@ -57,8 +59,9 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
unsafe fn compat53_pushglobalfuncname(
L: *mut lua_State,
level: c_int,
ar: *mut lua_Debug,
ar: &mut lua_Debug,
) -> c_int {
debug_assert_ne!(lua_checkstack(L, 2), 0);
let top = lua_gettop(L);
// push function
lua_getinfo(L, level, cstr!("f"), ar);
@ -73,10 +76,11 @@ unsafe fn compat53_pushglobalfuncname(
}
}
unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) {
if !(*ar).name.is_null() {
unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: &mut lua_Debug) {
debug_assert_ne!(lua_checkstack(L, 1), 0);
if !ar.name.is_null() {
// is there a name?
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
lua_pushfstring(L, cstr!("function '%s'"), ar.name);
} else if compat53_pushglobalfuncname(L, level, ar) != 0 {
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
lua_remove(L, -2); // remove name
@ -85,6 +89,17 @@ unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_De
}
}
unsafe fn compat53_pushfuncname_heap(_L: *mut lua_State, _level: c_int, ar: &mut lua_Debug, buf: &mut String) {
use std::fmt::Write;
if !ar.name.is_null() {
// is there a name?
_ = write!(buf, "function {:?}", CStr::from_ptr(ar.name));
} else {
buf.push('?');
}
}
//
// lua ported functions
//
@ -416,10 +431,22 @@ pub unsafe fn luaL_traceback(
0
};
debug_assert_ne!(lua_checkstack(L, 1), 0);
//let stack_req = if msg.is_null() { 0 } else { 1 } + 4 * (mark+1);
let stack_req = if msg.is_null() { 0 } else { 1 } + 4;
if lua_checkstack(L, stack_req) == 0 {
lua_pushliteral(L, "[not enough stack space]");
return;
}
if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg);
}
lua_pushliteral(L, "stack traceback:");
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
if level + 1 == mark {
// too many levels?
@ -440,6 +467,51 @@ pub unsafe fn luaL_traceback(
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_traceback_heap(
L: *mut lua_State,
L1: *mut lua_State,
msg: Option<String>,
mut level: c_int,
) -> String {
use std::fmt::Write;
let mut ar: lua_Debug = mem::zeroed();
let numlevels = lua_stackdepth(L);
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
COMPAT53_LEVELS1
} else {
0
};
let mut buf = msg.unwrap_or_default();
if !buf.is_empty() {
buf.push('\n');
}
buf.push_str("stack traceback:");
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
if level + 1 == mark {
// too many levels?
buf.push_str("\n\t..."); // add a '...'
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
} else {
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
let src = CStr::from_ptr(ar.short_src);
if let Ok(src) = src.to_str() {
_ = write!(buf, "\n\t{}:", src);
} else {
_ = write!(buf, "\n\t{:?}:", src);
}
if ar.currentline > 0 {
_ = write!(buf, "{}:", ar.currentline);
}
buf.push_str(" in ");
compat53_pushfuncname_heap(L, level, &mut ar, &mut buf);
}
level += 1;
}
buf
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
@ -508,7 +580,7 @@ pub unsafe fn luaL_requiref(
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
if lua_getfield(L, -1, modname) == LUA_TNIL {
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushcfunction(L, openf, b"open\0".as_ptr().cast());
lua_pushstring(L, modname);
lua_call(L, 1, 1);
lua_pushvalue(L, -1);

77
mlua-sys/src/luau/lgc.rs Normal file
View File

@ -0,0 +1,77 @@
use crate::*;
const MASK_WHITE0: u8 = 0b1;
const MASK_WHITE1: u8 = 0b10;
const MASK_WHITE: u8 = MASK_WHITE0 | MASK_WHITE1;
const MASK_BLACK: u8 = 0b100;
const MASK_FIXED: u8 = 0b1000;
/// Possible states of the Garbage Collector
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum GCState {
Pause = 0,
Propagate = 1,
PropagateAgain = 2,
Atomic = 3,
Sweep = 4,
}
#[inline(always)]
unsafe fn is_black(o: GCObjectPtr) -> bool {
(&*o.0).gch.marked & MASK_BLACK != 0
}
#[inline(always)]
unsafe fn is_white(o: GCObjectPtr) -> bool {
(&*o.0).gch.marked & MASK_WHITE != 0
}
/*#[inline(always)]
unsafe fn is_dead(g: *mut global_State, o: *mut GCObject) -> bool {
(&*o).gch.marked & (MASK_WHITE | MASK_FIXED) == otherwhite(g) & MASK_WHITE
}
#[inline(always)]
unsafe fn otherwhite(g: *mut global_State) -> u8 {
(&*g).currentwhite ^ MASK_WHITE
}
#[inline(always)]
fn black2gray(o: *mut GCObject) {
(&mut *o).gch.marked &= !MASK_BLACK;
}
pub unsafe fn luaC_barrierback(L: *mut lua_State, o: *mut GCObject, gclist: *mut *mut GCObject) {
let g = (&*L).global;
debug_assert!(is_black(o) && !is_dead(g, o) != 0);
debug_assert_ne!(g.gcstate, GCState::Pause);
black2gray(o); // make object gray (again)
*gclist = (&*g).grayagain;
(&mut *g).grayagain = o;
}*/
extern "C" {
pub fn luaC_barrierback(L: *mut lua_State, o: GCObjectPtr, gclist: *mut *mut GCObject);
pub fn luaC_barriertable(L: *mut lua_State, t: *mut Table, v: GCObjectPtr);
}
#[inline(always)]
pub unsafe fn luaC_threadbarrier(L: *mut lua_State) {
if is_black(L.into()) {
luaC_barrierback(L, L.into(), &mut (&mut *L).gc_list as *mut _);
}
}
#[inline(always)]
pub unsafe fn luaC_barriert(L: *mut lua_State, t: *mut Table, v: *mut TValue) {
if (*v).is_collectable() {
debug_assert_eq!((*v).tt, (*(*v).value.gc).gch.tt.into(), "types do not match");
let v = (*v).value.gc.into();
if is_black(v) && is_white(v) {
luaC_barriertable(L, t, v);
}
}
}

View File

@ -0,0 +1,263 @@
use std::ffi::{c_char, c_float, c_int, c_void};
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
use crate::{lua_CFunction, lua_Continuation, lua_Integer, lua_Number, GCObject, LUA_TTABLE, LUA_TNIL, LUA_TNUMBER, LUA_TLIGHTUSERDATA, LUA_TSTRING};
#[cfg(not(feature = "luau-vector4"))]
pub const LUA_VECTOR_SIZE: usize = 3;
#[cfg(feature = "luau-vector4")]
pub const LUA_VECTOR_SIZE: usize = 4;
pub const LUA_EXTRA_SIZE: usize = LUA_VECTOR_SIZE - 2;
opaque! {
pub type LuaNode;
pub type Proto;
pub type TString;
pub type Udata;
pub type UpVal;
}
pub type StkId = *mut TValue;
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct GCheader {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub union Value {
pub gc: *mut GCObject,
/// Light user data
pub p: *mut c_void,
/// Number
pub n: lua_Number,
pub b: c_int,
/// Vector
///
/// `v[0]`, `v[1]` live here; `v[2]`, possibly `v[3]` lives in TValue::extra
pub v: [c_float; 2],
bits: u64,
}
impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:b}", unsafe { self.bits })
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct TValue {
pub value: Value,
pub extra: [c_int; LUA_EXTRA_SIZE],
pub tt: c_int,
}
impl TValue {
#[inline(always)]
fn check_type(&self, tt: c_int) {
debug_assert_eq!(self.tt, tt, "Wrong type");
}
#[inline(always)]
pub unsafe fn as_table(&self) -> *mut Table {
self.check_type(LUA_TTABLE);
unsafe { self.value.gc.cast() }
}
#[inline(always)]
pub unsafe fn as_light_user_data(&self) -> *mut c_void {
self.check_type(LUA_TLIGHTUSERDATA);
unsafe { self.value.p }
}
#[inline(always)]
pub unsafe fn as_number(&self) -> lua_Number {
self.check_type(LUA_TNUMBER);
unsafe { self.value.n }
}
/// The only check if for rounding error, not type.
#[inline(always)]
pub unsafe fn try_as_integer(&self) -> Option<c_int> {
let n = self.as_number();
let n_int = n as lua_Integer;
if (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
Some(n_int)
} else {
None
}
}
#[inline(always)]
pub unsafe fn as_integer(&self) -> lua_Integer {
self.as_number() as lua_Integer
}
#[inline(always)]
pub fn is_collectable(&self) -> bool {
self.tt >= LUA_TSTRING
}
}
#[repr(C)]
pub struct Closure {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
pub is_c: u8,
pub n_upvals: u8,
pub stack_size: u8,
pub preload: u8,
pub gc_list: *mut GCObject,
pub env: *mut Table,
pub body: ClosureBody,
}
impl Closure {
#[inline(always)]
pub fn is_c(&self) -> bool {
self.is_c != 0
}
}
impl std::fmt::Debug for Closure {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Closure")
.field("tt", &self.tt)
.field("marked", &self.marked)
.field("memcat", &self.memcat)
.field("is_c", &self.is_c)
.field("n_upvals", &self.n_upvals)
.field("stack_size", &self.stack_size)
.field("preload", &self.preload)
.field("gc_List", &self.gc_list)
.field("env", &self.env)
.field("body", if self.is_c() {
unsafe { &self.body.c }
} else {
unsafe { &self.body.l }
})
.finish()
}
}
#[repr(C)]
pub union ClosureBody {
pub c: ManuallyDrop<ClosureC>,
pub l: ManuallyDrop<ClosureL>,
}
#[repr(C)]
pub struct ClosureC {
pub f: lua_CFunction,
pub cont: lua_Continuation,
pub debugname: *const c_char,
pub upvals: [TValue; 1],
}
impl std::fmt::Debug for ClosureC {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ClosureC")
.field("f", &self.f)
.field("cont", &self.cont)
.field("debugname", unsafe { &std::ffi::CStr::from_ptr(self.debugname) })
.field("upvals", &std::ptr::addr_of!(self.upvals))
.finish()
}
}
#[repr(C)]
pub struct ClosureL {
pub p: *mut Proto,
pub uprefs: [TValue; 1],
}
impl std::fmt::Debug for ClosureL {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ClosureL")
.field("p", &self.p)
.field("uprefs", &std::ptr::addr_of!(self.uprefs))
.finish()
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Table {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
/// 1<<p means tagmethod(p) is not present
pub tmcache: u8,
/// sandboxing feature to prohibit writes to table
pub readonly: bool,
/// environment doesn't share globals with other scripts
pub safeenv: bool,
/// log2 of size of `node' array
pub l_size_node: u8,
/// (1<<lsizenode)-1, truncated to 8 bits
pub node_mask8: u8,
/// size of `array' array
pub size_array: c_int,
/// lastfree: any free position is before this position
/// aboundary: negated 'boundary' of `array' array; iff aboundary < 0
pub lastfree_or_aboundary: c_int,
pub metatable: Option<NonNull<Table>>,
/// array part
pub array: Option<NonNull<TValue>>,
/// dict part. If there is no node, the value will be the address of [`crate::luaH_dummynode`].
pub node: NonNull<LuaNode>,
pub gc_list: *mut GCObject,
}
impl Table {
#[inline(always)]
pub unsafe fn array(&self) -> &[TValue] {
debug_assert!(self.size_array >= 0);
debug_assert_eq!(self.array.is_none(), self.size_array == 0, "inconsistent array properties: null pointer with non-zero length");
match (self.array, self.size_array) {
(None, 0) => &[],
// in debug_assertions this will be caught, otherwise, allow the optimizer to ignore it
(Some(_), 0) | (None, _) => std::hint::unreachable_unchecked(),
(Some(ptr), len) => std::slice::from_raw_parts(ptr.as_ptr(), len as _)
}
}
#[inline(always)]
pub unsafe fn array_mut(&mut self) -> &mut [TValue] {
debug_assert!(self.size_array >= 0);
debug_assert_eq!(self.array.is_none(), self.size_array == 0, "inconsistent array properties: null pointer with non-zero length");
match (self.array, self.size_array) {
(None, 0) => &mut [],
// in debug_assertions this will be caught, otherwise, allow the optimizer to ignore it
(Some(_), 0) | (None, _) => std::hint::unreachable_unchecked(),
(Some(ptr), len) => std::slice::from_raw_parts_mut(ptr.as_ptr(), len as _)
}
}
#[inline(always)]
pub unsafe fn array_lsize(&self) -> usize {
self.array().iter().rposition(|v| v.tt != LUA_TNIL).map(|i| i+1).unwrap_or(0)
}
}

216
mlua-sys/src/luau/lstate.rs Normal file
View File

@ -0,0 +1,216 @@
use std::ffi::{c_void, c_int, c_ushort, c_uint};
use std::mem::ManuallyDrop;
use std::ptr::{addr_of_mut, NonNull};
use crate::{Closure, GCheader, Proto, StkId, Table, TString, TValue, Udata, UpVal};
opaque! {
pub type global_State;
pub type Instruction;
}
extern "C" {
pub static luaO_nilobject_: TValue;
}
#[inline(always)]
pub fn luaO_nilobject() -> NonNull<TValue> {
unsafe { NonNull::new_unchecked(std::ptr::addr_of!(luaO_nilobject_).cast_mut()) }
}
/// Union of all collectible objects
#[repr(C)]
pub union GCObject
{
pub gch: ManuallyDrop<GCheader>,
pub ts: ManuallyDrop<TString>,
pub u: ManuallyDrop<Udata>,
pub cl: ManuallyDrop<Closure>,
pub h: ManuallyDrop<Table>,
pub p: ManuallyDrop<Proto>,
pub uv: ManuallyDrop<UpVal>,
/// thread
pub th: ManuallyDrop<lua_State>,
}
impl std::fmt::Debug for GCObject {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "GCObject({:?})", unsafe { self.gch })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct GCObjectPtr(pub *mut GCObject);
impl GCObjectPtr {
pub fn hdr(self) -> *mut GCheader {
unsafe { addr_of_mut!(*(*self.0).gch) }
}
}
impl From<GCObjectPtr> for *mut GCObject {
#[inline(always)]
fn from(value: GCObjectPtr) -> Self {
value.0
}
}
impl From<*mut GCObject> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut GCObject) -> Self {
Self(value)
}
}
impl From<*mut GCheader> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut GCheader) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut TString> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut TString) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut Udata> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut Udata) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut Closure> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut Closure) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut Table> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut Table) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut Proto> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut Proto) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut UpVal> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut UpVal) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
impl From<*mut lua_State> for GCObjectPtr {
#[inline(always)]
fn from(value: *mut lua_State) -> Self {
unsafe { GCObjectPtr(std::mem::transmute(value)) }
}
}
/// See docs at <https://github.com/Roblox/luau/blob/a02eb78c96f695a4287e7dd1beaccab3642c35f3/VM/src/lstate.h#L29-L29>
#[derive(Debug)]
#[repr(C)]
pub struct CallInfo {
/// base for this function.
pub base: StkId,
/// function index in the stack
pub func: StkId,
/// top for this function
///
/// Seems to indicate the allocated stack size, not used.
pub top: StkId,
pub saved_pc: *const Instruction,
/// expected number of results from this function
pub n_results: c_int,
/// call frame flags, see LUA_CALLINFO_*
pub flags: c_uint,
}
/// should the interpreter return after returning from this callinfo? first frame must have this set
pub const LUA_CALLINFO_RETURN: c_uint = 1 << 0;
/// should the error thrown during execution get handled by continuation from this callinfo? func must be C
pub const LUA_CALLINFO_HANDLE: c_uint = 1 << 1;
/// should this function be executed using execution callback for native code
pub const LUA_CALLINFO_NATIVE: c_uint = 1 << 2;
/// A raw Lua state associated with a thread.
#[derive(Debug)]
#[repr(C)]
pub struct lua_State {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
pub status: u8,
/// memory category that is used for new GC object allocations
pub active_memcat: u8,
/// thread is currently executing, stack may be mutated without barriers
pub is_active: bool,
/// call debugstep hook after each instruction
pub single_step: bool,
/// first free slot in the stack
pub top: StkId,
/// base of current function
pub base: StkId,
pub global: *mut global_State,
/// call info for current function
pub ci: *mut CallInfo,
/// last free slot in the stack
pub stack_last: StkId,
/// stack base
pub stack: StkId,
/// points after end of ci array
pub end_ci: *mut CallInfo,
/// array of [`CallInfo`]s
pub base_ci: *mut CallInfo,
/// The current allocated size of the entire thread stack.
pub stack_size: c_int,
/// size of array [`base_ci']
pub size_ci: c_int,
/// number of nested C calls
pub n_c_calls: c_ushort,
/// nested C calls when resuming coroutine
pub base_c_calls: c_ushort,
/// when table operations or `INDEX`/`NEWINDEX` is invoked from Luau, what is the expected slot for lookup?
pub cached_slot: c_int,
/// table of globals
pub globals: *mut Table,
/// list of open upvalues in this stack
pub open_upval: *mut UpVal,
pub gc_list: *mut GCObject,
/// when invoked from Luau using `NAMECALL`, what method do we need to invoke?
pub namecall: *mut TString,
pub userdata: *mut c_void,
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}

View File

@ -0,0 +1,5 @@
use crate::LuaNode;
extern "C" {
pub static luaH_dummynode: LuaNode;
}

View File

@ -1,6 +1,5 @@
//! Contains definitions from `lua.h`.
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
use std::ptr;
@ -31,13 +30,9 @@ pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRERR: c_int = 5;
pub const LUA_BREAK: c_int = 6;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
pub use super::lstate::lua_State;
//
// Basic types
@ -381,25 +376,19 @@ pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) {
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
}
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosurek(L, f, ptr::null(), 0, None)
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction, debug_name: *const c_char) {
lua_pushcclosurek(L, f, debug_name, 0, None)
}
pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) {
lua_pushcclosurek(L, f, debugname, 0, None)
}
pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) {
lua_pushcclosurek(L, f, ptr::null(), nup, None)
}
pub unsafe fn lua_pushcclosured(
#[inline(always)]
pub unsafe fn lua_pushcclosure(
L: *mut lua_State,
f: lua_CFunction,
debugname: *const c_char,
nup: c_int,
debug_name: *const c_char,
n_up: c_int,
) {
lua_pushcclosurek(L, f, debugname, nup, None)
lua_pushcclosurek(L, f, debug_name, n_up, None)
}
#[inline(always)]
@ -516,3 +505,13 @@ pub struct lua_Callbacks {
extern "C" {
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
}
#[inline(always)]
pub unsafe fn real_index2addr(l: *mut lua_State, idx: i32) -> ptr::NonNull<crate::TValue> {
debug_assert!(idx > 0, "Invalid stack index: {idx}");
let l = &*l;
let o = l.base.add(idx as usize - 1);
debug_assert!(o < (&*l.ci).top);
debug_assert!(o < l.top);
ptr::NonNull::new_unchecked(o)
}

15
mlua-sys/src/luau/luau.rs Normal file
View File

@ -0,0 +1,15 @@
use std::ffi::{c_char, c_int, c_void};
use crate::lua_State;
pub type luau_AssertHandler = unsafe extern "C" fn(expr: *const c_char, file: *const c_char, line: c_int, function: *const c_char) -> c_int;
pub type luau_Pfunc = unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void);
extern "C" {
#[cfg(debug_assertions)]
#[link_name = "_ZZN4Luau13assertHandlerEvE7handler"]
pub static mut luau_assertHandler: luau_AssertHandler;
pub fn luaD_pcall(L: *mut lua_State, func: luau_Pfunc, ud: *mut c_void, old_top: isize, ef: isize);
}

View File

@ -2,14 +2,24 @@
pub use compat::*;
pub use lauxlib::*;
pub use lgc::*;
pub use lobject::*;
pub use lstate::*;
pub use ltable::*;
pub use lua::*;
pub use luacode::*;
pub use luacodegen::*;
pub use lualib::*;
pub use luau::*;
pub mod compat;
pub mod lauxlib;
pub mod lgc;
pub mod lobject;
pub mod lstate;
pub mod ltable;
pub mod lua;
pub mod luacode;
pub mod luacodegen;
pub mod lualib;
pub mod luau;

View File

@ -5,3 +5,13 @@ macro_rules! cstr {
as *const ::std::os::raw::c_char
};
}
macro_rules! opaque {
($($vis:vis type $name:ident;)*) => {$(
#[repr(C)]
$vis struct $name {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
)*};
}

View File

@ -145,7 +145,7 @@ impl<'lua> Function<'lua> {
let _sg = StackGuard::new(state);
check_stack(state, nargs + 3)?;
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback, "mlua::util::error_traceback\0".as_ptr().cast()));
let stack_start = ffi::lua_gettop(state);
lua.push_ref(&self.0);
for arg in args.drain_all() {
@ -278,7 +278,7 @@ impl<'lua> Function<'lua> {
lua.push_value(arg)?;
}
protect_lua!(state, nargs + 1, 1, fn(state) {
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
ffi::lua_pushcclosure(state, args_wrapper_impl, "mlua::Function::bind::args_wrapper_impl\0".as_ptr().cast(), ffi::lua_gettop(state));
})?;
Function(lua.pop_ref())

View File

@ -85,7 +85,7 @@ mod error;
pub mod ffi;
mod function;
mod hook;
mod lua;
pub mod lua;
#[cfg(feature = "luau")]
mod luau;
mod memory;

View File

@ -78,7 +78,7 @@ pub struct LuaInner {
}
// Data associated with the Lua.
pub(crate) struct ExtraData {
pub struct ExtraData {
// Same layout as `Lua`
inner: MaybeUninit<Arc<LuaInner>>,
@ -110,7 +110,7 @@ pub(crate) struct ExtraData {
thread_pool: Vec<c_int>,
// Address of `WrappedFailure` metatable
wrapped_failure_mt_ptr: *const c_void,
pub wrapped_failure_mt_ptr: *const c_void,
// Waker for polling futures
#[cfg(feature = "async")]
@ -412,6 +412,7 @@ impl Lua {
(*extra).libs |= libs;
if !options.catch_rust_panics {
eprintln!("overriding pcall");
mlua_expect!(
(|| -> Result<()> {
let _sg = StackGuard::new(state);
@ -421,10 +422,10 @@ impl Lua {
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
ffi::lua_pushcfunction(state, safe_pcall);
ffi::lua_pushcfunction(state, safe_pcall, "mlua::safe_pcall\0".as_ptr().cast());
rawset_field(state, -2, "pcall")?;
ffi::lua_pushcfunction(state, safe_xpcall);
ffi::lua_pushcfunction(state, safe_xpcall, "mlua::safe_xpcall\0".as_ptr().cast());
rawset_field(state, -2, "xpcall")?;
Ok(())
@ -1559,10 +1560,15 @@ impl Lua {
/// # Safety
/// This function is unsafe because provides a way to execute unsafe C function.
#[inline]
pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> {
pub unsafe fn create_c_function(
&self,
func: ffi::lua_CFunction,
cont: Option<ffi::lua_Continuation>,
debug_name: Option<&'static CStr>,
) -> Result<Function> {
let state = self.state();
check_stack(state, 1)?;
ffi::lua_pushcfunction(state, func);
ffi::lua_pushcclosurek(state, func, debug_name.map(CStr::as_ptr).unwrap_or(std::ptr::null()), 0, cont);
Ok(Function(self.pop_ref()))
}
@ -1574,10 +1580,18 @@ impl Lua {
pub unsafe fn create_c_closure<'lua>(
&'lua self,
func: ffi::lua_CFunction,
cont: Option<ffi::lua_Continuation>,
debug_name: Option<&'static CStr>,
upvalues: impl IntoLuaMulti<'lua>,
) -> Result<Function> {
let state = self.state();
if matches!(upvalues.lua_multi_len(), Some(0)) {
return self.create_c_function(func, cont, debug_name);
}
let upvalues = upvalues.into_lua_multi(self)?;
if upvalues.len() == 0 {
return self.create_c_function(func, cont, debug_name);
}
let Ok(n) = i32::try_from(upvalues.len()) else {
return Err(Error::BadArgument { to: Some("create_c_closure".to_owned()), pos: 3, name: Some("upvalues".to_owned()), cause: Arc::new(Error::BindError) })
};
@ -1586,7 +1600,7 @@ impl Lua {
for upvalue in upvalues {
self.push_value(upvalue)?;
}
ffi::lua_pushcclosure(state, func, n);
ffi::lua_pushcclosurek(state, func, debug_name.map(CStr::as_ptr).unwrap_or(std::ptr::null()), n, cont);
Ok(Function(self.pop_ref()))
}
@ -2433,26 +2447,10 @@ impl Lua {
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())),
ffi::LUA_TUSERDATA => {
let wrapped_failure_mt_ptr = (*self.extra.get()).wrapped_failure_mt_ptr;
// We must prevent interaction with userdata types other than UserData OR a WrappedError.
// WrappedPanics are automatically resumed.
match get_gc_userdata::<WrappedFailure>(state, -1, wrapped_failure_mt_ptr).as_mut()
{
Some(WrappedFailure::Error(err)) => {
let err = err.clone();
ffi::lua_pop(state, 1);
Value::Error(err)
}
Some(WrappedFailure::Panic(panic)) => {
if let Some(panic) = panic.take() {
ffi::lua_pop(state, 1);
resume_unwind(panic);
}
// Previously resumed panic?
ffi::lua_pop(state, 1);
Nil
}
_ => Value::UserData(AnyUserData(self.pop_ref())),
match (*self.extra.get()).pop_wrapped_failure(state) {
Some(Some(e)) => Value::Error(e),
Some(None) => Nil,
None => Value::UserData(AnyUserData(self.pop_ref())),
}
}
@ -2800,10 +2798,10 @@ impl Lua {
push_gc_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
if protect {
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
ffi::lua_pushcclosure(state, call_callback, "mlua::call_callback\0".as_ptr().cast(), 1);
})?;
} else {
ffi::lua_pushcclosure(state, call_callback, 1);
ffi::lua_pushcclosure(state, call_callback, "mlua::call_callback\0".as_ptr().cast(), 1);
}
Ok(Function(self.pop_ref()))
@ -2852,10 +2850,10 @@ impl Lua {
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
if protect {
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, poll_future, 1);
ffi::lua_pushcclosure(state, poll_future, "mlua::poll_future\0".as_ptr().cast(), 1);
})?;
} else {
ffi::lua_pushcclosure(state, poll_future, 1);
ffi::lua_pushcclosure(state, poll_future, "mlua::poll_future\0".as_ptr().cast(), 1);
}
Ok(1)
@ -2909,10 +2907,10 @@ impl Lua {
push_gc_userdata(state, upvalue, protect)?;
if protect {
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
ffi::lua_pushcclosure(state, call_callback, "mlua::call_callback\0".as_ptr().cast(), 1);
})?;
} else {
ffi::lua_pushcclosure(state, call_callback, 1);
ffi::lua_pushcclosure(state, call_callback, "mlua::call_callback\0".as_ptr().cast(), 1);
}
Function(self.pop_ref())
@ -2933,7 +2931,7 @@ impl Lua {
env.set("get_poll", get_poll)?;
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
unsafe {
env.set("unpack", self.create_c_function(unpack)?)?;
env.set("unpack", self.create_c_function(unpack, None, Some(CStr::from_bytes_with_nul_unchecked(b"unpack\0")))?)?;
}
env.set("pending", {
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut c_void)
@ -3142,6 +3140,31 @@ impl ExtraData {
pub(crate) fn mem_state(&self) -> NonNull<MemoryState> {
self.mem_state.unwrap()
}
/// Uses 2 stack spaces, does not call checkstack
#[inline]
pub unsafe fn pop_wrapped_failure(&self, state: *mut ffi::lua_State) -> Option<Option<Error>> {
// We must prevent interaction with userdata types other than UserData OR a WrappedError.
// WrappedPanics are automatically resumed.
match get_gc_userdata::<WrappedFailure>(state, -1, self.wrapped_failure_mt_ptr).as_mut()
{
Some(WrappedFailure::Error(err)) => {
let err = err.clone();
ffi::lua_pop(state, 1);
Some(Some(err))
}
Some(WrappedFailure::Panic(panic)) => {
if let Some(panic) = panic.take() {
ffi::lua_pop(state, 1);
resume_unwind(panic);
}
// Previously resumed panic?
ffi::lua_pop(state, 1);
Some(None)
}
_ => None,
}
}
}
struct StateGuard<'a>(&'a LuaInner, *mut ffi::lua_State);
@ -3160,12 +3183,12 @@ impl<'a> Drop for StateGuard<'a> {
}
#[cfg(feature = "luau")]
unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
pub unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
(*ffi::lua_callbacks(state)).userdata as *mut ExtraData
}
#[cfg(not(feature = "luau"))]
unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
pub unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
// `ExtraData` can be null only when Lua state is foreign.

View File

@ -17,10 +17,10 @@ impl Lua {
globals.raw_set(
"collectgarbage",
self.create_c_function(lua_collectgarbage)?,
self.create_c_function(lua_collectgarbage, None, Some(CStr::from_bytes_with_nul_unchecked(b"collectgarbage\0")))?,
)?;
globals.raw_set("require", self.create_function(lua_require)?)?;
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
globals.raw_set("vector", self.create_c_function(lua_vector, None, Some(CStr::from_bytes_with_nul_unchecked(b"vector\0")))?)?;
// Set `_VERSION` global to include version number
// The environment variable `LUAU_VERSION` set by the build script

View File

@ -105,8 +105,13 @@ macro_rules! require_module_feature {
}
macro_rules! protect_lua {
(@debug_name) => {
concat!("protect_lua[", file!(), ":", stringify!(line!()), ":", stringify!(column!()), "]")
.as_ptr().cast()
};
($state:expr, $nargs:expr, $nresults:expr, $f:expr) => {
crate::util::protect_lua_closure($state, $nargs, $nresults, $f)
crate::util::protect_lua_closure($state, $nargs, $nresults, $f, $crate::macros::protect_lua!(@debug_name))
};
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
@ -120,6 +125,7 @@ macro_rules! protect_lua {
}
}
crate::util::protect_lua_call($state, $nargs, do_call)
crate::util::protect_lua_call($state, $nargs, do_call, $crate::macros::protect_lua!(@debug_name))
}};
}
pub(crate) use protect_lua;

View File

@ -30,6 +30,11 @@ impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
v.push_front(self.into_lua(lua)?);
Ok(v)
}
#[inline(always)]
fn lua_multi_len(&self) -> Option<usize> {
Some(1)
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
@ -58,6 +63,11 @@ impl<'lua> IntoLuaMulti<'lua> for MultiValue<'lua> {
fn into_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(self)
}
#[inline(always)]
fn lua_multi_len(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
@ -145,6 +155,11 @@ impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
Ok(values)
}
#[inline(always)]
fn lua_multi_len(&self) -> Option<usize> {
Some(self.0.len())
}
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
@ -167,6 +182,11 @@ macro_rules! impl_tuple {
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_pooled(lua))
}
#[inline(always)]
fn lua_multi_len(&self) -> Option<usize> {
Some(0)
}
}
impl<'lua> FromLuaMulti<'lua> for () {
@ -193,6 +213,11 @@ macro_rules! impl_tuple {
push_reverse!(results, $($name.into_lua(lua)?,)*);
Ok(results)
}
#[inline(always)]
fn lua_multi_len(&self) -> Option<usize> {
Some(count!($last $($name)*))
}
}
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
@ -236,6 +261,15 @@ macro_rules! push_reverse {
($multi_value:expr,) => ();
}
macro_rules! count {
($a:ident) => {
1
};
($a:ident $($b:ident)+) => {
1 + count!($($b)+)
}
}
impl_tuple!();
impl_tuple!(A);
impl_tuple!(A B);

View File

@ -60,8 +60,8 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
///
/// Requires `feature = "async"`
///
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[must_use = "futures do nothing unless you `.await` or poll them"]
@ -284,8 +284,8 @@ impl<'lua> Thread<'lua> {
///
/// Requires `feature = "async"`
///
/// [`Future`]: futures_core::future::Future
/// [`Stream`]: futures_core::stream::Stream
/// [`Future`]: std::future::Future
/// [`Stream`]: futures_util::stream::Stream
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
///
/// # Examples

View File

@ -307,7 +307,7 @@ impl<'lua> LuaRef<'lua> {
impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({})", self.index)
write!(f, "Ref({}, {:p})", self.index, self.to_pointer())
}
}

View File

@ -23,8 +23,8 @@ static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
map
});
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message.
/// Checks that Lua has enough free stack space for future stack operations. On failure, this will
/// panic with an internal error message.
#[inline]
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
@ -36,7 +36,7 @@ pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
);
}
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
/// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
#[inline]
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
if ffi::lua_checkstack(state, amount) == 0 {
@ -78,22 +78,23 @@ impl Drop for StackGuard {
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
// should not contain any values that implements Drop.
// Internally uses 2 extra stack spaces, and does not call checkstack.
/// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
/// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
/// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
/// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
/// should not contain any values that implements Drop.
/// Internally uses 2 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua_call(
state: *mut ffi::lua_State,
nargs: c_int,
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
debug_name: *const c_char,
) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
ffi::lua_pushcfunction(state, error_traceback, "mlua::util::error_traceback\0".as_ptr().cast());
ffi::lua_pushcfunction(state, f, debug_name);
});
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
@ -109,18 +110,19 @@ pub unsafe fn protect_lua_call(
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
// given function return type is not the return value count, instead the inner function return
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
// will generally be lonjmping, should not contain any values that implements Drop.
// Internally uses 3 extra stack spaces, and does not call checkstack.
/// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
/// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
/// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
/// given function return type is not the return value count, instead the inner function return
/// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
/// will generally be lonjmping, should not contain any values that implements Drop.
/// Internally uses 3 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State,
nargs: c_int,
nresults: c_int,
f: F,
debug_name: *const c_char,
) -> Result<R>
where
F: Fn(*mut ffi::lua_State) -> R,
@ -152,8 +154,8 @@ where
let stack_start = ffi::lua_gettop(state) - nargs;
MemoryState::relax_limit_with(state, || {
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, do_call::<F, R>);
ffi::lua_pushcfunction(state, error_traceback, "mlua::util::error_traceback\0".as_ptr().cast());
ffi::lua_pushcfunction(state, do_call::<F, R>, debug_name);
});
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
@ -178,12 +180,12 @@ where
}
}
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
// error at the top of the stack:
// 1) If the error is actually a WrappedPanic, this will continue the panic.
// 2) If the error on the top of the stack is actually a WrappedError, just returns it.
// 3) Otherwise, interprets the error as the appropriate lua error.
// Uses 2 stack spaces, does not call checkstack.
/// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
/// error at the top of the stack:
/// 1) If the error is actually a WrappedPanic, this will continue the panic.
/// 2) If the error on the top of the stack is actually a WrappedError, just returns it.
/// 3) Otherwise, interprets the error as the appropriate lua error.
/// Uses 2 stack spaces, does not call checkstack.
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
mlua_debug_assert!(
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
@ -233,7 +235,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
}
}
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
/// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
#[inline(always)]
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
// Always use protected mode if the string is too long
@ -247,7 +249,7 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
}
}
// Uses 3 stack spaces, does not call checkstack.
/// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(
state: *mut ffi::lua_State,
@ -263,7 +265,7 @@ pub unsafe fn push_table(
}
}
// Uses 4 stack spaces, does not call checkstack.
/// Uses 4 stack spaces, does not call checkstack.
pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
ffi::lua_pushvalue(state, table);
protect_lua!(state, 2, 0, |state| {
@ -273,7 +275,7 @@ pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str
})
}
// Internally uses 3 stack spaces, does not call checkstack.
/// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(not(feature = "luau"))]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
@ -288,7 +290,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool)
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
/// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "luau")]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
@ -309,7 +311,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool)
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
/// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub unsafe fn push_userdata_uv<T>(
@ -336,10 +338,10 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
ud
}
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
// previously invalidated, and this method does not check for this.
// Uses 1 extra stack space and does not call checkstack.
/// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
/// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
/// previously invalidated, and this method does not check for this.
/// Uses 1 extra stack space and does not call checkstack.
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// We set the metatable of userdata on __gc to a special table with no __gc method and with
// metamethods that trigger an error on access. We do this so that it will not be double
@ -357,8 +359,8 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
ptr::read(ud)
}
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack.
/// Pushes the userdata and attaches a metatable with __gc method.
/// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
@ -371,7 +373,7 @@ pub unsafe fn push_gc_userdata<T: Any>(
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
/// Uses 2 stack spaces, does not call checkstack
pub unsafe fn get_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
index: c_int,
@ -467,9 +469,9 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
if ret != ffi::LUA_OK {
ffi::lua_error(state);
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_pushcfunction(state, lua_istable_impl);
ffi::lua_pushcfunction(state, lua_error_impl, "mlua::util::lua_error_impl\0".as_ptr().cast());
ffi::lua_pushcfunction(state, lua_isfunction_impl, "mlua::util::lua_isfunction_impl\0".as_ptr().cast());
ffi::lua_pushcfunction(state, lua_istable_impl, "mlua::util::lua_istable_impl\0".as_ptr().cast());
ffi::lua_call(state, 3, 1);
#[cfg(feature = "luau-jit")]
@ -521,8 +523,8 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
if ret != ffi::LUA_OK {
ffi::lua_error(state);
}
ffi::lua_pushcfunction(state, lua_error_impl);
ffi::lua_pushcfunction(state, lua_isfunction_impl);
ffi::lua_pushcfunction(state, lua_error_impl, "mlua::util::lua_error_impl\0".as_ptr().cast());
ffi::lua_pushcfunction(state, lua_isfunction_impl, "mlua::util::lua_isfunction_impl\0".as_ptr().cast());
ffi::lua_call(state, 2, 1);
#[cfg(feature = "luau-jit")]
@ -536,14 +538,14 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
})
}
// Populates the given table with the appropriate members to be a userdata metatable for the given type.
// This function takes the given table at the `metatable` index, and adds an appropriate `__gc` member
// to it for the given type and a `__metatable` entry to protect the table from script access.
// The function also, if given a `field_getters` or `methods` tables, will create an `__index` metamethod
// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
// captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
// Internally uses 9 stack spaces and does not call checkstack.
/// Populates the given table with the appropriate members to be a userdata metatable for the given type.
/// This function takes the given table at the `metatable` index, and adds an appropriate `__gc` member
/// to it for the given type and a `__metatable` entry to protect the table from script access.
/// The function also, if given a `field_getters` or `methods` tables, will create an `__index` metamethod
/// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
/// captured `__index` if no matches found.
/// The same is also applicable for `__newindex` metamethod and `field_setters` table.
/// Internally uses 9 stack spaces and does not call checkstack.
pub unsafe fn init_userdata_metatable<T>(
state: *mut ffi::lua_State,
metatable: c_int,
@ -618,17 +620,17 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
0
}
// In the context of a lua callback, this will call the given function and if the given function
// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
// the Rust side, it will resume the panic.
//
// This function assumes the structure of the stack at the beginning of a callback, that the only
// elements on the stack are the arguments to the callback.
//
// This function uses some of the bottom of the stack for error handling, the given callback will be
// given the number of arguments available as an argument, and should return the number of returns
// as normal, but cannot assume that the arguments available start at 0.
/// In the context of a lua callback, this will call the given function and if the given function
/// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
/// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
/// the Rust side, it will resume the panic.
///
/// This function assumes the structure of the stack at the beginning of a callback, that the only
/// elements on the stack are the arguments to the callback.
///
/// This function uses some of the bottom of the stack for error handling, the given callback will be
/// given the number of arguments available as an argument, and should return the number of returns
/// as normal, but cannot assume that the arguments available start at 0.
#[inline]
pub unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
where
@ -709,7 +711,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
1
}
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
/// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
// Move error object to the main thread to safely call `__tostring` metamethod if present
ffi::lua_xmove(thread, state, 1);
@ -723,7 +725,7 @@ pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ff
}
}
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
/// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
@ -749,7 +751,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
}
}
// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`.
/// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`.
pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
@ -775,7 +777,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
}
ffi::lua_pushvalue(state, 2);
ffi::lua_pushcclosure(state, xpcall_msgh, 1);
ffi::lua_pushcclosure(state, xpcall_msgh, "mlua::util::xpcall_msgh\0".as_ptr().cast(), 1);
ffi::lua_copy(state, 1, 2);
ffi::lua_replace(state, 1);
@ -795,8 +797,8 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
}
}
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
// Does not call lua_checkstack, uses 1 stack space.
/// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
/// Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
@ -820,8 +822,8 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
Some(ffi::lua_mainthread(state))
}
// Initialize the internal (with __gc method) metatable for a type T.
// Uses 6 stack spaces and calls checkstack.
/// Initialize the internal (with __gc method) metatable for a type T.
/// Uses 6 stack spaces and calls checkstack.
pub unsafe fn init_gc_metatable<T: Any>(
state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
@ -860,7 +862,7 @@ pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
}
// Initialize the error, panic, and destructed userdata metatables.
/// Initialize the error, panic, and destructed userdata metatables.
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
check_stack(state, 7)?;
@ -917,7 +919,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
init_gc_metatable::<WrappedFailure>(
state,
Some(|state| {
ffi::lua_pushcfunction(state, error_tostring);
ffi::lua_pushcfunction(state, error_tostring, "mlua::util::error_tostring\0".as_ptr().cast());
rawset_field(state, -2, "__tostring")
}),
)?;
@ -929,7 +931,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
}
push_table(state, 0, 26, true)?;
ffi::lua_pushcfunction(state, destructed_error);
ffi::lua_pushcfunction(state, destructed_error, "mlua::util::destructed_error\0".as_ptr().cast());
for &method in &[
"__add",
"__sub",
@ -1019,8 +1021,8 @@ impl WrappedFailure {
}
}
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
// panicking.
/// Converts the given lua value to a string in a reasonable format without causing a Lua error or
/// panicking.
pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
match ffi::lua_type(state, index) {
ffi::LUA_TNONE => "<none>".to_string(),

View File

@ -1,5 +1,5 @@
//! Mostly copied from bevy_utils
//! https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
//! <https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs>
use std::any::type_name;

View File

@ -500,6 +500,12 @@ impl<'lua> MultiValue<'lua> {
pub trait IntoLuaMulti<'lua> {
/// Performs the conversion.
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
/// The exact length of the [`MultiValue`] produced by [`IntoLuaMulti::into_lua_multi`], if known.
#[inline(always)]
fn lua_multi_len(&self) -> Option<usize> {
None
}
}
/// Trait for types that can be created from an arbitrary number of Lua values.

View File

@ -92,7 +92,7 @@ fn test_c_function() -> Result<()> {
0
}
let func = unsafe { lua.create_c_function(c_function)? };
let func = unsafe { lua.create_c_function(c_function, None)? };
func.call(())?;
assert_eq!(lua.globals().get::<_, bool>("c_function")?, true);