Turn more comments into doc comments, improve debuggability
This commit is contained in:
parent
58e5c7b18b
commit
061f84f702
|
@ -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 }
|
||||
|
|
|
@ -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))]
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)>,
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
use crate::LuaNode;
|
||||
|
||||
extern "C" {
|
||||
pub static luaH_dummynode: LuaNode;
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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)>,
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
|
|
95
src/lua.rs
95
src/lua.rs
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
34
src/multi.rs
34
src/multi.rs
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
154
src/util/mod.rs
154
src/util/mod.rs
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue