Compare commits
6 Commits
4dd12531d9
...
96575e6ab5
Author | SHA1 | Date |
---|---|---|
Michael Pfaff | 96575e6ab5 | |
Michael Pfaff | 11d81209d9 | |
Alex Orlenko | 925a2816cc | |
Alex Orlenko | b3b8d79446 | |
Alex Orlenko | 85f17a269d | |
Alex Orlenko | b169031d4e |
|
@ -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);
|
||||
|
@ -397,6 +400,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
|||
res
|
||||
}
|
||||
|
||||
// TODO: why not just checkstack and concat all at the end?
|
||||
pub unsafe fn luaL_traceback(
|
||||
L: *mut lua_State,
|
||||
L1: *mut lua_State,
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -38,30 +38,45 @@ impl<'lua> FromLua<'lua> for Value<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<String<'lua>> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: String<'lua>) -> Self {
|
||||
Value::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "String",
|
||||
message: Some("expected string or number".to_string()),
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<String<'lua>> {
|
||||
match value {
|
||||
Value::String(s) => Ok(s),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "string",
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<Table<'lua>> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: Table<'lua>) -> Self {
|
||||
Value::Table(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,10 +112,17 @@ impl<'lua> FromLua<'lua> for OwnedTable {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<Function<'lua>> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: Function<'lua>) -> Self {
|
||||
Value::Function(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,10 +173,17 @@ impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<Thread<'lua>> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: Thread<'lua>) -> Self {
|
||||
Value::Thread(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,10 +201,17 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<AnyUserData<'lua>> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: AnyUserData<'lua>) -> Self {
|
||||
Value::UserData(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,49 +268,67 @@ impl<'lua, T: 'static> FromLua<'lua> for UserDataRefMut<'lua, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<Error> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: Error) -> Self {
|
||||
Value::Error(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Error(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Error> {
|
||||
match value {
|
||||
Value::Error(err) => Ok(err),
|
||||
val => Ok(Error::RuntimeError(
|
||||
lua.coerce_string(val)?
|
||||
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
|
||||
.unwrap_or_else(|| "<unprintable error>".to_owned()),
|
||||
)),
|
||||
_ => Err(Error::FromLuaConversionError { from: value.type_name(), to: "error", message: None }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<bool> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: bool) -> Self {
|
||||
Value::Boolean(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Boolean(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match v {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Nil => Ok(false),
|
||||
Value::Boolean(b) => Ok(b),
|
||||
_ => Ok(true),
|
||||
_ => Err(Error::FromLuaConversionError { from: value.type_name(), to: "boolean", message: None }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> From<LightUserData> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: LightUserData) -> Self {
|
||||
Value::LightUserData(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'lua> IntoLua<'lua> for LightUserData {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::LightUserData(self))
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,6 +346,15 @@ impl<'lua> FromLua<'lua> for LightUserData {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl<'lua> From<crate::types::Vector> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: crate::types::Vector) -> Self {
|
||||
Value::Vector(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl<'lua> IntoLua<'lua> for crate::types::Vector {
|
||||
#[inline]
|
||||
|
@ -325,16 +388,8 @@ impl<'lua> IntoLua<'lua> for StdString {
|
|||
impl<'lua> FromLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "String",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned())
|
||||
let s = String::from_lua(value, lua)?;
|
||||
Ok(s.to_str()?.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,17 +417,7 @@ impl<'lua> IntoLua<'lua> for Box<str> {
|
|||
impl<'lua> FromLua<'lua> for Box<str> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Box<str>",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned()
|
||||
.into_boxed_str())
|
||||
StdString::from_lua(value, lua).map(|s| s.into_boxed_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,23 +431,15 @@ impl<'lua> IntoLua<'lua> for CString {
|
|||
impl<'lua> FromLua<'lua> for CString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let string = lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
let s = String::from_lua(value, lua)?;
|
||||
match CStr::from_bytes_with_nul(s.as_bytes_with_nul()) {
|
||||
Ok(s) => Ok(s.to_owned()),
|
||||
Err(e) => Err(Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "CString",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?;
|
||||
|
||||
match CStr::from_bytes_with_nul(string.as_bytes_with_nul()) {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "CString",
|
||||
message: Some("invalid C-style string".to_string()),
|
||||
message: Some(e.to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,17 +467,8 @@ impl<'lua> IntoLua<'lua> for BString {
|
|||
impl<'lua> FromLua<'lua> for BString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(BString::from(
|
||||
lua.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "String",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
))
|
||||
let s = String::from_lua(value, lua)?;
|
||||
Ok(BString::from(s.as_bytes().to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,9 +479,20 @@ impl<'lua> IntoLua<'lua> for &BStr {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_int_infallible {
|
||||
($($x:ty),*) => {
|
||||
$(impl<'lua> From<$x> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from(value: $x) -> Self {
|
||||
Self::Integer(value.into())
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_int {
|
||||
($x:ty) => {
|
||||
impl<'lua> IntoLua<'lua> for $x {
|
||||
($($x:ty),*) => {
|
||||
$(impl<'lua> IntoLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
cast(self)
|
||||
|
@ -470,53 +509,32 @@ macro_rules! lua_convert_int {
|
|||
|
||||
impl<'lua> FromLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
(match value {
|
||||
Value::Integer(i) => cast(i),
|
||||
Value::Number(n) => cast(n),
|
||||
_ => {
|
||||
if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
}
|
||||
})?)
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: stringify!($x),
|
||||
message: None,
|
||||
}),
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
from: value.type_name(),
|
||||
to: stringify!($x),
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
lua_convert_int!(i8);
|
||||
lua_convert_int!(u8);
|
||||
lua_convert_int!(i16);
|
||||
lua_convert_int!(u16);
|
||||
lua_convert_int!(i32);
|
||||
lua_convert_int!(u32);
|
||||
lua_convert_int!(i64);
|
||||
lua_convert_int!(u64);
|
||||
lua_convert_int!(i128);
|
||||
lua_convert_int!(u128);
|
||||
lua_convert_int!(isize);
|
||||
lua_convert_int!(usize);
|
||||
lua_convert_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
|
||||
lua_convert_int_infallible!(i8, u8, i16, u16, i32);
|
||||
|
||||
macro_rules! lua_convert_float {
|
||||
($x:ty) => {
|
||||
impl<'lua> IntoLua<'lua> for $x {
|
||||
($($x:ty),*) => {
|
||||
$(impl<'lua> IntoLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
cast(self)
|
||||
|
@ -531,28 +549,27 @@ macro_rules! lua_convert_float {
|
|||
|
||||
impl<'lua> FromLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_number(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
(match value {
|
||||
Value::Integer(i) => cast(i),
|
||||
Value::Number(n) => cast(n),
|
||||
_ => return Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: stringify!($x),
|
||||
message: Some("expected number or string coercible to number".to_string()),
|
||||
})
|
||||
.and_then(|n| {
|
||||
cast(n).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
message: Some("number out of range".to_string()),
|
||||
})
|
||||
})
|
||||
message: None,
|
||||
}),
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: stringify!($x),
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
lua_convert_float!(f32);
|
||||
lua_convert_float!(f64);
|
||||
lua_convert_float!(f32, f64);
|
||||
|
||||
impl<'lua, T> IntoLua<'lua> for &[T]
|
||||
where
|
||||
|
@ -709,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))
|
||||
|
@ -736,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))
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
pub use ::ffi::*;
|
||||
|
||||
use crate::util::{push_gc_userdata, WrappedFailure};
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn mlua_pusherror(state: *mut lua_State, err: crate::Error) -> crate::Result<()> {
|
||||
push_gc_userdata(state, WrappedFailure::Error(err), false)
|
||||
}
|
|
@ -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,16 @@ 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
|
||||
}
|
||||
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
/// The function's return values are converted to the generic type `R`.
|
||||
|
@ -391,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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
48
src/hook.rs
48
src/hook.rs
|
@ -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)]
|
||||
|
|
|
@ -82,6 +82,7 @@ mod macros;
|
|||
mod chunk;
|
||||
mod conversion;
|
||||
mod error;
|
||||
pub mod ffi;
|
||||
mod function;
|
||||
mod hook;
|
||||
mod lua;
|
||||
|
@ -103,7 +104,7 @@ mod value;
|
|||
|
||||
pub mod prelude;
|
||||
|
||||
pub use ffi::{lua_CFunction, lua_State};
|
||||
pub use self::ffi::{lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
|
|
64
src/lua.rs
64
src/lua.rs
|
@ -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) {
|
||||
|
@ -1558,6 +1558,7 @@ 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> {
|
||||
let state = self.state();
|
||||
check_stack(state, 1)?;
|
||||
|
@ -1565,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
|
||||
|
@ -2281,8 +2302,8 @@ impl Lua {
|
|||
extra.app_data.remove()
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> {
|
||||
/// Uses 2 stack spaces, does not call checkstack
|
||||
pub unsafe fn push_value(&self, value: Value) -> Result<()> {
|
||||
let state = self.state();
|
||||
match value {
|
||||
Value::Nil => {
|
||||
|
@ -2342,8 +2363,8 @@ impl Lua {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub(crate) unsafe fn pop_value(&self) -> Value {
|
||||
/// Uses 2 stack spaces, does not call checkstack
|
||||
pub unsafe fn pop_value(&self) -> Value {
|
||||
let state = self.state();
|
||||
match ffi::lua_type(state, -1) {
|
||||
ffi::LUA_TNIL => {
|
||||
|
@ -2444,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"
|
||||
|
@ -2453,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());
|
||||
|
@ -2798,8 +2819,7 @@ impl Lua {
|
|||
))]
|
||||
unsafe {
|
||||
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
||||
load_from_std_lib(self.main_state, StdLib::COROUTINE)?;
|
||||
(*self.extra.get()).libs |= StdLib::COROUTINE;
|
||||
return Err(Error::RuntimeError("StdLib::COROUTINE is not loaded".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,14 +69,23 @@ macro_rules! mlua_debug_assert {
|
|||
};
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
macro_rules! mlua_expect {
|
||||
($res:expr, $msg:expr) => {
|
||||
($res:expr, $msg:expr $(,)?) => {
|
||||
$res.expect(bug_msg!($msg))
|
||||
};
|
||||
}
|
||||
|
||||
($res:expr, $msg:expr,) => {
|
||||
mlua_expect!($res, $msg)
|
||||
};
|
||||
#[cfg(not(debug_assertions))]
|
||||
macro_rules! mlua_expect {
|
||||
($res:expr, $msg:expr $(,)?) => {{
|
||||
let x;
|
||||
#[allow(unused_unsafe)]
|
||||
{
|
||||
x = unsafe { $res.into_iter().next().unwrap_unchecked() };
|
||||
}
|
||||
x
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "module")]
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -41,6 +41,16 @@ 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
|
||||
}
|
||||
|
||||
/// Get a `&str` slice if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -139,8 +149,7 @@ impl<'lua> String<'lua> {
|
|||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
|
|
103
src/table.rs
103
src/table.rs
|
@ -45,8 +45,17 @@ 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
|
||||
}
|
||||
|
||||
/// Sets a key-value pair in the table.
|
||||
///
|
||||
/// If the value is `nil`, this will effectively remove the pair.
|
||||
|
@ -150,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() {
|
||||
|
@ -179,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() {
|
||||
|
@ -492,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.
|
||||
|
@ -557,6 +598,7 @@ impl<'lua> Table<'lua> {
|
|||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[inline]
|
||||
pub fn set_readonly(&self, enabled: bool) {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
|
@ -568,11 +610,22 @@ impl<'lua> Table<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_safe(&self, enabled: bool) {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
let enabled = enabled as _;
|
||||
unsafe {
|
||||
ffi::lua_setreadonly(ref_thread, self.0.index, enabled);
|
||||
ffi::lua_setsafeenv(ref_thread, self.0.index, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `readonly` attribute of the table.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[inline]
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
|
@ -586,8 +639,7 @@ impl<'lua> Table<'lua> {
|
|||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
|
@ -641,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
|
||||
///
|
||||
|
@ -685,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> {
|
||||
|
@ -715,7 +748,6 @@ impl<'lua> Table<'lua> {
|
|||
table: self.0,
|
||||
index: Some(1),
|
||||
len: Some(len),
|
||||
raw: true,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -737,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(())
|
||||
}
|
||||
|
@ -1051,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)?;
|
||||
}
|
||||
|
@ -1141,7 +1174,6 @@ pub struct TableSequence<'lua, V> {
|
|||
table: LuaRef<'lua>,
|
||||
index: Option<Integer>,
|
||||
len: Option<Integer>,
|
||||
raw: bool,
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
|
@ -1158,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()))),
|
||||
}
|
||||
|
|
|
@ -73,6 +73,16 @@ 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
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// Equivalent to `coroutine.resume`.
|
||||
|
@ -88,6 +98,8 @@ impl<'lua> Thread<'lua> {
|
|||
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
|
||||
/// `return`s values from its main function, returns those.
|
||||
///
|
||||
/// Also returns `true` if the coroutine is yielded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
@ -114,7 +126,7 @@ impl<'lua> Thread<'lua> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn resume<A, R>(&self, args: A) -> Result<R>
|
||||
pub fn resume<A, R>(&self, args: A) -> Result<(bool, R)>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
|
@ -124,7 +136,7 @@ impl<'lua> Thread<'lua> {
|
|||
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let (yielded, results) = unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, cmp::max(nargs + 1, 3))?;
|
||||
|
||||
|
@ -164,9 +176,9 @@ impl<'lua> Thread<'lua> {
|
|||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
results
|
||||
(ret == ffi::LUA_YIELD, results)
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
R::from_lua_multi(results, lua).map(|result| (yielded, result))
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
|
@ -428,9 +440,9 @@ where
|
|||
// This is safe as we are not moving the whole struct
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let ret: MultiValue = if let Some(args) = this.args0.take() {
|
||||
this.thread.resume(args?)?
|
||||
this.thread.resume(args?)?.1
|
||||
} else {
|
||||
this.thread.resume(())?
|
||||
this.thread.resume(())?.1
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
|
@ -462,9 +474,9 @@ where
|
|||
// This is safe as we are not moving the whole struct
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let ret: MultiValue = if let Some(args) = this.args0.take() {
|
||||
this.thread.resume(args?)?
|
||||
this.thread.resume(args?)?.1
|
||||
} else {
|
||||
this.thread.resume(())?
|
||||
this.thread.resume(())?.1
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
|
|
12
src/types.rs
12
src/types.rs
|
@ -273,13 +273,14 @@ impl RegistryKey {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
pub struct LuaRef<'lua> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
}
|
||||
|
||||
impl<'lua> LuaRef<'lua> {
|
||||
#[inline(always)]
|
||||
pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self {
|
||||
LuaRef {
|
||||
lua,
|
||||
|
@ -296,6 +297,12 @@ impl<'lua> LuaRef<'lua> {
|
|||
mem::forget(self);
|
||||
owned_ref
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const std::os::raw::c_void {
|
||||
let ref_thread = self.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.index) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> fmt::Debug for LuaRef<'lua> {
|
||||
|
@ -305,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);
|
||||
|
@ -319,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();
|
||||
|
|
|
@ -806,6 +806,16 @@ 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
|
||||
}
|
||||
|
||||
/// Checks whether the type of this userdata is `T`.
|
||||
pub fn is<T: 'static>(&self) -> bool {
|
||||
match self.inspect(|_: &UserDataCell<T>| Ok(())) {
|
||||
|
|
|
@ -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;
|
||||
|
@ -359,6 +360,7 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
|||
|
||||
// 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,
|
||||
t: T,
|
||||
|
@ -398,7 +400,7 @@ pub unsafe fn get_gc_userdata<T: Any>(
|
|||
}
|
||||
|
||||
unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
|
@ -628,6 +630,7 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
|
|||
// 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
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
|
@ -655,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);
|
||||
|
@ -667,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);
|
||||
|
||||
|
@ -678,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)
|
||||
|
@ -854,6 +853,7 @@ pub unsafe fn init_gc_metatable<T: Any>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr =
|
||||
|
@ -1066,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;
|
||||
|
|
20
src/value.rs
20
src/value.rs
|
@ -116,18 +116,14 @@ impl<'lua> Value<'lua> {
|
|||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
unsafe {
|
||||
match self {
|
||||
Value::LightUserData(ud) => ud.0,
|
||||
Value::Table(t) => t.to_pointer(),
|
||||
Value::String(s) => s.to_pointer(),
|
||||
Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => {
|
||||
ffi::lua_topointer(r.lua.ref_thread(), r.index)
|
||||
}
|
||||
_ => ptr::null(),
|
||||
}
|
||||
match self {
|
||||
Value::LightUserData(ud) => ud.0,
|
||||
Value::Table(Table(r))
|
||||
| Value::String(String(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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")]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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))
|
||||
})?;
|
||||
|
|
Loading…
Reference in New Issue