Compare commits

..

6 Commits

Author SHA1 Message Date
Michael Pfaff 96575e6ab5
More inline, make some methods and constructors public, add create_c_closure 2023-07-06 13:06:17 -04:00
Michael Pfaff 11d81209d9
API changes, inline more often 2023-07-06 13:06:08 -04:00
Alex Orlenko 925a2816cc
clippy 2023-07-06 00:59:46 +01:00
Alex Orlenko b3b8d79446
Make Debug interface more user friendly
- use String instead of Vec<u8>
- update docs
- unify fields between lua5.x/luau
- line numbers are `usize`
2023-07-06 00:38:33 +01:00
Alex Orlenko 85f17a269d
Add `Table:is_empty()` function 2023-06-26 10:50:18 +01:00
Alex Orlenko b169031d4e
Don't use any metamethods in `Table::sequence_values()` iterator.
This is matches with `Table::pair()` iterator.
Remove `Table::raw_sequence_values()` iterator.
2023-06-26 10:46:59 +01:00
17 changed files with 229 additions and 142 deletions

View File

@ -11,6 +11,7 @@ use super::lauxlib::*;
use super::lua::*;
use super::luacode::*;
#[inline(always)]
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
while a < b {
lua_pushvalue(L, a);
@ -88,6 +89,7 @@ unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_De
// lua ported functions
//
#[inline(always)]
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
@ -135,6 +137,7 @@ pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
#[inline]
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);

View File

@ -17,6 +17,7 @@ pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXCSTACK - 2000;
pub const LUA_ENVIRONINDEX: c_int = -LUAI_MAXCSTACK - 2001;
pub const LUA_GLOBALSINDEX: c_int = -LUAI_MAXCSTACK - 2002;
#[inline(always)]
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_GLOBALSINDEX - i
}
@ -85,6 +86,7 @@ pub type lua_Alloc = unsafe extern "C" fn(
) -> *mut c_void;
/// Returns Luau release version (eg. `0.xxx`).
#[inline(always)]
pub const fn luau_version() -> Option<&'static str> {
option_env!("LUAU_VERSION")
}

View File

@ -726,7 +726,7 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))
@ -753,7 +753,7 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|res| res.map(|(k, _)| k))

View File

@ -10,7 +10,8 @@ use crate::memory::MemoryState;
use crate::table::Table;
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::util::{
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
assert_stack, check_stack, error_traceback, linenumber_to_usize, pop_error, ptr_to_lossy_str,
ptr_to_str, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
@ -52,24 +53,22 @@ impl OwnedFunction {
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
#[derive(Clone, Debug)]
pub struct FunctionInfo {
/// A (reasonable) name of the function.
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<String>,
/// Explains the `name` field ("global", "local", "method", "field", "upvalue", or "").
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<String>,
/// A string "Lua" if the function is a Lua function, "C" if it is a C function, "main" if it is the main part of a chunk.
pub what: Option<String>,
/// The source of the chunk that created the function.
pub source: Option<Vec<u8>>,
/// A "printable" version of source, to be used in error messages.
pub short_src: Option<Vec<u8>>,
pub name_what: Option<&'static str>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
pub what: &'static str,
/// Source of the chunk that created the function.
pub source: Option<String>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<String>,
/// The line number where the definition of the function starts.
pub line_defined: i32,
/// The line number where the definition of the function ends.
///
/// Always `-1` for Luau.
pub last_line_defined: i32,
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
}
/// Luau function coverage snapshot.
@ -84,6 +83,12 @@ pub struct CoverageInfo {
}
impl<'lua> Function<'lua> {
/// Unsafe because we omit type checks.
#[inline]
pub unsafe fn from_ref(r: LuaRef<'lua>) -> Self {
Self(r)
}
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
@ -395,23 +400,25 @@ impl<'lua> Function<'lua> {
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
FunctionInfo {
name: ptr_to_cstr_bytes(ar.name).map(|s| String::from_utf8_lossy(s).into_owned()),
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes(ar.namewhat)
.map(|s| String::from_utf8_lossy(s).into_owned()),
name_what: match ptr_to_str(ar.namewhat) {
Some("") => None,
val => val,
},
#[cfg(feature = "luau")]
name_what: None,
what: ptr_to_cstr_bytes(ar.what).map(|s| String::from_utf8_lossy(s).into_owned()),
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
what: ptr_to_str(ar.what).unwrap_or("main"),
source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
#[cfg(feature = "luau")]
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
line_defined: ar.linedefined,
short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
line_defined: linenumber_to_usize(ar.linedefined),
#[cfg(not(feature = "luau"))]
last_line_defined: ar.lastlinedefined,
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: -1,
last_line_defined: None,
}
}
}

View File

@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::cell::UnsafeCell;
#[cfg(not(feature = "luau"))]
use std::ops::{BitOr, BitOrAssign};
@ -6,7 +7,7 @@ use std::os::raw::c_int;
use ffi::lua_Debug;
use crate::lua::Lua;
use crate::util::ptr_to_cstr_bytes;
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
/// Contains information about currently executing Lua code.
///
@ -78,9 +79,12 @@ impl<'lua> Debug<'lua> {
);
DebugNames {
name: ptr_to_cstr_bytes((*self.ar.get()).name),
name: ptr_to_lossy_str((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
Some("") => None,
val => val,
},
#[cfg(feature = "luau")]
name_what: None,
}
@ -102,15 +106,17 @@ impl<'lua> Debug<'lua> {
);
DebugSource {
source: ptr_to_cstr_bytes((*self.ar.get()).source),
source: ptr_to_lossy_str((*self.ar.get()).source),
#[cfg(not(feature = "luau"))]
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
#[cfg(feature = "luau")]
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
line_defined: (*self.ar.get()).linedefined,
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
#[cfg(not(feature = "luau"))]
last_line_defined: (*self.ar.get()).lastlinedefined,
what: ptr_to_cstr_bytes((*self.ar.get()).what),
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
#[cfg(feature = "luau")]
last_line_defined: None,
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
}
}
}
@ -210,18 +216,26 @@ pub enum DebugEvent {
#[derive(Clone, Debug)]
pub struct DebugNames<'a> {
pub name: Option<&'a [u8]>,
pub name_what: Option<&'a [u8]>,
/// A (reasonable) name of the function (`None` if the name cannot be found).
pub name: Option<Cow<'a, str>>,
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
///
/// Always `None` for Luau.
pub name_what: Option<&'static str>,
}
#[derive(Clone, Debug)]
pub struct DebugSource<'a> {
pub source: Option<&'a [u8]>,
pub short_src: Option<&'a [u8]>,
pub line_defined: i32,
#[cfg(not(feature = "luau"))]
pub last_line_defined: i32,
pub what: Option<&'a [u8]>,
/// Source of the chunk that created the function.
pub source: Option<Cow<'a, str>>,
/// A "printable" version of `source`, to be used in error messages.
pub short_src: Option<Cow<'a, str>>,
/// The line number where the definition of the function starts.
pub line_defined: Option<usize>,
/// The line number where the definition of the function ends (not set by Luau).
pub last_line_defined: Option<usize>,
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
pub what: &'static str,
}
#[derive(Copy, Clone, Debug)]

View File

@ -448,7 +448,7 @@ impl Lua {
///
/// Once called, a returned Lua state is cached in the registry and can be retrieved
/// by calling this function again.
#[allow(clippy::missing_safety_doc)]
#[allow(clippy::missing_safety_doc, clippy::arc_with_non_send_sync)]
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
assert!(!state.is_null(), "Lua state is NULL");
if let Some(lua) = Lua::try_from_ptr(state) {
@ -1566,6 +1566,26 @@ impl Lua {
Ok(Function(self.pop_ref()))
}
/// Wraps a C function, creating a callable Lua function handle to it.
///
/// # Safety
/// This function is unsafe because provides a way to execute unsafe C function.
#[inline]
pub unsafe fn create_c_closure<'lua>(&'lua self, func: ffi::lua_CFunction, upvalues: impl IntoLuaMulti<'lua>) -> Result<Function> {
let state = self.state();
let upvalues = upvalues.into_lua_multi(self)?;
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) })
};
// number of upvalues plus 1 extra needed by push_value for each
check_stack(state, n + 1)?;
for upvalue in upvalues {
self.push_value(upvalue)?;
}
ffi::lua_pushcclosure(state, func, n);
Ok(Function(self.pop_ref()))
}
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
///
/// While executing the function Rust will poll Future and if the result is not ready, call
@ -2283,7 +2303,7 @@ impl Lua {
}
/// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> {
pub unsafe fn push_value(&self, value: Value) -> Result<()> {
let state = self.state();
match value {
Value::Nil => {
@ -2344,7 +2364,7 @@ impl Lua {
}
/// Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn pop_value(&self) -> Value {
pub unsafe fn pop_value(&self) -> Value {
let state = self.state();
match ffi::lua_type(state, -1) {
ffi::LUA_TNIL => {
@ -2445,8 +2465,8 @@ impl Lua {
}
}
// Pushes a LuaRef value onto the stack, uses 1 stack space, does not call checkstack
pub(crate) unsafe fn push_ref(&self, lref: &LuaRef) {
/// Pushes a LuaRef value onto the stack, uses 1 stack space, does not call checkstack
pub unsafe fn push_ref(&self, lref: &LuaRef) {
assert!(
Arc::ptr_eq(&lref.lua.0, &self.0),
"Lua instance passed Value created from a different main Lua state"
@ -2454,28 +2474,28 @@ impl Lua {
ffi::lua_xpush(self.ref_thread(), self.state(), lref.index);
}
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
// preventing garbage collection until the returned `LuaRef` is dropped.
//
// References are stored in the stack of a specially created auxiliary thread that exists only
// to store reference values. This is much faster than storing these in the registry, and also
// much more flexible and requires less bookkeeping than storing them directly in the currently
// used stack. The implementation is somewhat biased towards the use case of a relatively small
// number of short term references being created, and `RegistryKey` being used for long term
// references.
pub(crate) unsafe fn pop_ref(&self) -> LuaRef {
/// Pops the topmost element of the stack and stores a reference to it. This pins the object,
/// preventing garbage collection until the returned `LuaRef` is dropped.
///
/// References are stored in the stack of a specially created auxiliary thread that exists only
/// to store reference values. This is much faster than storing these in the registry, and also
/// much more flexible and requires less bookkeeping than storing them directly in the currently
/// used stack. The implementation is somewhat biased towards the use case of a relatively small
/// number of short term references being created, and `RegistryKey` being used for long term
/// references.
pub unsafe fn pop_ref(&self) -> LuaRef {
ffi::lua_xmove(self.state(), self.ref_thread(), 1);
let index = ref_stack_pop(self.extra.get());
LuaRef::new(self, index)
}
// Same as `pop_ref` but assumes the value is already on the reference thread
pub(crate) unsafe fn pop_ref_thread(&self) -> LuaRef {
/// Same as `pop_ref` but assumes the value is already on the reference thread
pub unsafe fn pop_ref_thread(&self) -> LuaRef {
let index = ref_stack_pop(self.extra.get());
LuaRef::new(self, index)
}
pub(crate) fn clone_ref(&self, lref: &LuaRef) -> LuaRef {
pub fn clone_ref(&self, lref: &LuaRef) -> LuaRef {
unsafe {
ffi::lua_pushvalue(self.ref_thread(), lref.index);
let index = ref_stack_pop(self.extra.get());

View File

@ -237,7 +237,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
let len = t.raw_len() as usize;
let mut deserializer = SeqDeserializer {
seq: t.raw_sequence_values(),
seq: t.sequence_values(),
options: self.options,
visited: self.visited,
};

View File

@ -41,6 +41,12 @@ impl OwnedString {
}
impl<'lua> String<'lua> {
/// Unsafe because we omit type checks.
#[inline]
pub unsafe fn from_ref(r: LuaRef<'lua>) -> Self {
Self(r)
}
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}

View File

@ -45,8 +45,13 @@ impl OwnedTable {
}
}
#[allow(clippy::len_without_is_empty)]
impl<'lua> Table<'lua> {
/// Unsafe because we omit type checks.
#[inline]
pub unsafe fn from_ref(r: LuaRef<'lua>) -> Self {
Self(r)
}
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}
@ -154,11 +159,15 @@ impl<'lua> Table<'lua> {
}
/// Checks whether the table contains a non-nil value for `key`.
///
/// This might invoke the `__index` metamethod.
pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
Ok(self.get::<_, Value>(key)? != Value::Nil)
}
/// Appends a value to the back of the table.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
// Fast track
if !self.has_metatable() {
@ -183,6 +192,8 @@ impl<'lua> Table<'lua> {
}
/// Removes the last element from the table and returns it.
///
/// This might invoke the `__len` and `__newindex` metamethods.
pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
// Fast track
if !self.has_metatable() {
@ -496,6 +507,32 @@ impl<'lua> Table<'lua> {
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
}
/// Returns `true` if the table is empty, without invoking metamethods.
///
/// It checks both the array part and the hash part.
pub fn is_empty(&self) -> bool {
// Check array part
if self.raw_len() != 0 {
return false;
}
// Check hash part
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 4);
lua.push_ref(&self.0);
ffi::lua_pushnil(state);
if ffi::lua_next(state, -2) != 0 {
return false;
}
}
true
}
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
///
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
@ -656,12 +693,9 @@ impl<'lua> Table<'lua> {
/// Consume this table and return an iterator over all values in the sequence part of the table.
///
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
/// metatable will not be called.
///
/// Just like [`pairs`], the values are wrapped in a [`Result`].
/// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
/// encountered. This mirrors the behavior of Lua's `ipairs` function but does not invoke
/// any metamethods.
///
/// # Note
///
@ -700,28 +734,12 @@ impl<'lua> Table<'lua> {
table: self.0,
index: Some(1),
len: None,
raw: false,
_phantom: PhantomData,
}
}
/// Consume this table and return an iterator over all values in the sequence part of the table.
///
/// Unlike the `sequence_values`, does not invoke `__index` metamethod when iterating.
///
/// [`sequence_values`]: #method.sequence_values
pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
TableSequence {
table: self.0,
index: Some(1),
len: None,
raw: true,
_phantom: PhantomData,
}
}
#[cfg(feature = "serialize")]
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
pub(crate) fn sequence_values_by_len<V: FromLua<'lua>>(
self,
len: Option<Integer>,
) -> TableSequence<'lua, V> {
@ -730,7 +748,6 @@ impl<'lua> Table<'lua> {
table: self.0,
index: Some(1),
len: Some(len),
raw: true,
_phantom: PhantomData,
}
}
@ -752,10 +769,11 @@ impl<'lua> Table<'lua> {
lua.push_ref(&self.0);
lua.push_value(value)?;
let idx = idx.try_into().unwrap();
if lua.unlikely_memory_error() {
ffi::lua_rawseti(state, -2, idx as _);
ffi::lua_rawseti(state, -2, idx);
} else {
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx as _))?;
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
}
Ok(())
}
@ -1066,7 +1084,7 @@ impl<'lua> Serialize for Table<'lua> {
let len = self.raw_len() as usize;
if len > 0 || self.is_array() {
let mut seq = serializer.serialize_seq(Some(len))?;
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
for v in self.clone().sequence_values_by_len::<Value>(None) {
let v = v.map_err(serde::ser::Error::custom)?;
seq.serialize_element(&v)?;
}
@ -1156,7 +1174,6 @@ pub struct TableSequence<'lua, V> {
table: LuaRef<'lua>,
index: Option<Integer>,
len: Option<Integer>,
raw: bool,
_phantom: PhantomData<V>,
}
@ -1173,15 +1190,10 @@ where
let res = (|| unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 1 + if self.raw { 0 } else { 3 })?;
check_stack(state, 1)?;
lua.push_ref(&self.table);
let res = if self.raw {
ffi::lua_rawgeti(state, -1, index)
} else {
protect_lua!(state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
};
match res {
match ffi::lua_rawgeti(state, -1, index) {
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
_ => Ok(Some((index, lua.pop_value()))),
}

View File

@ -73,6 +73,12 @@ pub struct AsyncThread<'lua, R> {
}
impl<'lua> Thread<'lua> {
/// Unsafe because we omit type checks.
#[inline]
pub unsafe fn from_ref(r: LuaRef<'lua>) -> Self {
Self(r)
}
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}

View File

@ -280,6 +280,7 @@ pub struct LuaRef<'lua> {
}
impl<'lua> LuaRef<'lua> {
#[inline(always)]
pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self {
LuaRef {
lua,
@ -311,12 +312,14 @@ impl<'lua> fmt::Debug for LuaRef<'lua> {
}
impl<'lua> Clone for LuaRef<'lua> {
#[inline]
fn clone(&self) -> Self {
self.lua.clone_ref(self)
}
}
impl<'lua> Drop for LuaRef<'lua> {
#[inline]
fn drop(&mut self) {
if self.drop {
self.lua.drop_ref_index(self.index);
@ -325,6 +328,7 @@ impl<'lua> Drop for LuaRef<'lua> {
}
impl<'lua> PartialEq for LuaRef<'lua> {
#[inline]
fn eq(&self, other: &Self) -> bool {
let lua = self.lua;
let state = lua.state();

View File

@ -806,6 +806,12 @@ impl OwnedAnyUserData {
}
impl<'lua> AnyUserData<'lua> {
/// Unsafe because we omit type checks.
#[inline]
pub unsafe fn from_ref(r: LuaRef<'lua>) -> Self {
Self(r)
}
pub fn as_raw_ref(&self) -> &LuaRef<'lua> {
&self.0
}

View File

@ -1,11 +1,12 @@
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::ffi::CStr;
use std::fmt::Write;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc;
use std::{mem, ptr, slice};
use std::{mem, ptr, slice, str};
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
@ -657,8 +658,6 @@ where
Ok(Err(err)) => {
ffi::lua_settop(state, 1);
let wrapped_error = ud as *mut WrappedFailure;
// Build `CallbackError` with traceback
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, ptr::null(), 0);
@ -669,10 +668,8 @@ where
"<not enough stack space for traceback>".to_string()
};
let cause = Arc::new(err);
ptr::write(
wrapped_error,
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
);
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
ptr::write(ud, wrapped_error);
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
@ -680,7 +677,7 @@ where
}
Err(p) => {
ffi::lua_settop(state, 1);
ptr::write(ud as *mut WrappedFailure, WrappedFailure::Panic(Some(p)));
ptr::write(ud, WrappedFailure::Panic(Some(p)));
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
@ -1069,11 +1066,25 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
pub(crate) unsafe fn ptr_to_cstr_bytes<'a>(input: *const c_char) -> Option<&'a [u8]> {
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
if input.is_null() {
return None;
}
Some(CStr::from_ptr(input).to_bytes())
str::from_utf8(CStr::from_ptr(input).to_bytes()).ok()
}
pub(crate) unsafe fn ptr_to_lossy_str<'a>(input: *const c_char) -> Option<Cow<'a, str>> {
if input.is_null() {
return None;
}
Some(String::from_utf8_lossy(CStr::from_ptr(input).to_bytes()))
}
pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
match n {
n if n < 0 => None,
n => Some(n as usize),
}
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;

View File

@ -196,37 +196,37 @@ fn test_function_info() -> Result<()> {
let function1_info = function1.info();
#[cfg(feature = "luau")]
assert_eq!(function1_info.name.as_deref(), Some("function1"));
assert_eq!(function1_info.source.as_deref(), Some(b"source1".as_ref()));
assert_eq!(function1_info.line_defined, 2);
assert_eq!(function1_info.source.as_deref(), Some("source1"));
assert_eq!(function1_info.line_defined, Some(2));
#[cfg(not(feature = "luau"))]
assert_eq!(function1_info.last_line_defined, 4);
assert_eq!(function1_info.last_line_defined, Some(4));
#[cfg(feature = "luau")]
assert_eq!(function1_info.last_line_defined, -1);
assert_eq!(function1_info.what.as_deref(), Some("Lua"));
assert_eq!(function1_info.last_line_defined, None);
assert_eq!(function1_info.what, "Lua");
let function2_info = function2.info();
assert_eq!(function2_info.name, None);
assert_eq!(function2_info.source.as_deref(), Some(b"source1".as_ref()));
assert_eq!(function2_info.line_defined, 3);
assert_eq!(function2_info.source.as_deref(), Some("source1"));
assert_eq!(function2_info.line_defined, Some(3));
#[cfg(not(feature = "luau"))]
assert_eq!(function2_info.last_line_defined, 3);
assert_eq!(function2_info.last_line_defined, Some(3));
#[cfg(feature = "luau")]
assert_eq!(function2_info.last_line_defined, -1);
assert_eq!(function2_info.what.as_deref(), Some("Lua"));
assert_eq!(function2_info.last_line_defined, None);
assert_eq!(function2_info.what, "Lua");
let function3_info = function3.info();
assert_eq!(function3_info.name, None);
assert_eq!(function3_info.source.as_deref(), Some(b"=[C]".as_ref()));
assert_eq!(function3_info.line_defined, -1);
assert_eq!(function3_info.last_line_defined, -1);
assert_eq!(function3_info.what.as_deref(), Some("C"));
assert_eq!(function3_info.source.as_deref(), Some("=[C]"));
assert_eq!(function3_info.line_defined, None);
assert_eq!(function3_info.last_line_defined, None);
assert_eq!(function3_info.what, "C");
let print_info = globals.get::<_, Function>("print")?.info();
#[cfg(feature = "luau")]
assert_eq!(print_info.name.as_deref(), Some("print"));
assert_eq!(print_info.source.as_deref(), Some(b"=[C]".as_ref()));
assert_eq!(print_info.what.as_deref(), Some("C"));
assert_eq!(print_info.line_defined, -1);
assert_eq!(print_info.source.as_deref(), Some("=[C]"));
assert_eq!(print_info.what, "C");
assert_eq!(print_info.line_defined, None);
Ok(())
}

View File

@ -2,7 +2,6 @@
use std::cell::RefCell;
use std::ops::Deref;
use std::str;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Mutex};
@ -61,9 +60,8 @@ fn test_function_calls() -> Result<()> {
assert_eq!(debug.event(), DebugEvent::Call);
let names = debug.names();
let source = debug.source();
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
hook_output.lock().unwrap().push((name, what));
let name = names.name.map(|s| s.into_owned());
hook_output.lock().unwrap().push((name, source.what));
Ok(())
})?;
@ -80,18 +78,12 @@ fn test_function_calls() -> Result<()> {
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
assert_eq!(
*output,
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("Lua".to_string()))
]
vec![(None, "main"), (Some("len".to_string()), "Lua")]
);
} else {
assert_eq!(
*output,
vec![
(None, Some("main".to_string())),
(Some("len".to_string()), Some("C".to_string()))
]
vec![(None, "main"), (Some("len".to_string()), "C")]
);
}

View File

@ -1,7 +1,7 @@
use mlua::{Error, Lua, Nil, Result, Table, TableExt, Value};
#[test]
fn test_set_get() -> Result<()> {
fn test_globals_set_get() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
@ -43,6 +43,7 @@ fn test_table() -> Result<()> {
let table3 = globals.get::<_, Table>("table3")?;
assert_eq!(table1.len()?, 5);
assert!(!table1.is_empty());
assert_eq!(
table1
.clone()
@ -60,6 +61,7 @@ fn test_table() -> Result<()> {
assert_eq!(table1, [1, 2, 3, 4, 5]);
assert_eq!(table2.len()?, 0);
assert!(table2.is_empty());
assert_eq!(
table2
.clone()
@ -141,7 +143,7 @@ fn test_table_push_pop() -> Result<()> {
assert_eq!(
table2
.clone()
.raw_sequence_values::<i64>()
.sequence_values::<i64>()
.collect::<Result<Vec<_>>>()?,
vec![]
);
@ -192,8 +194,10 @@ fn test_table_clear() -> Result<()> {
)
.eval::<Table>()?;
assert_eq!(t2.raw_len(), 3);
assert!(!t2.is_empty());
t2.clear()?;
assert_eq!(t2.raw_len(), 0);
assert!(t2.is_empty());
assert_eq!(t2.raw_get::<_, Value>("a")?, Value::Nil);
assert_ne!(t2.get_metatable(), None);

View File

@ -1226,8 +1226,8 @@ fn test_inspect_stack() -> Result<()> {
let logline = lua.create_function(|lua, msg: StdString| {
let debug = lua.inspect_stack(1).unwrap(); // caller
let source = debug.source().short_src.map(core::str::from_utf8);
let source = source.transpose().unwrap().unwrap_or("?");
let source = debug.source().short_src;
let source = source.as_deref().unwrap_or("?");
let line = debug.curr_line();
Ok(format!("{}:{} {}", source, line, msg))
})?;