Backport changes from rlua 0.16 (master branch)

This commit is contained in:
Alex Orlenko 2019-09-27 17:27:37 +01:00
parent 53b352466e
commit affa85feb0
20 changed files with 1001 additions and 693 deletions

View file

@ -2,6 +2,7 @@
name = "rlua" name = "rlua"
version = "0.15.3" version = "0.15.3"
authors = ["kyren <catherine@chucklefish.org>"] authors = ["kyren <catherine@chucklefish.org>"]
edition = "2018"
description = "High level bindings to Lua 5.3" description = "High level bindings to Lua 5.3"
repository = "https://github.com/chucklefish/rlua" repository = "https://github.com/chucklefish/rlua"
documentation = "https://docs.rs/rlua" documentation = "https://docs.rs/rlua"
@ -14,17 +15,17 @@ license = "MIT"
[dependencies] [dependencies]
libc = { version = "0.2" } libc = { version = "0.2" }
failure = { version = "0.1.2" }
num-traits = { version = "0.2.6" } num-traits = { version = "0.2.6" }
compiletest_rs = { version = "0.3", optional = true } bstr = {version = "0.2", features = ["std"], default_features = false }
[build-dependencies] [build-dependencies]
cc = "1.0" cc = { version = "1.0" }
pkg-config = "0.3.11" pkg-config = { version = "0.3.11" }
[dev-dependencies] [dev-dependencies]
rustyline = "2.0.0" rustyline = "5.0"
criterion = "0.2.0" criterion = "0.2.0"
compiletest_rs = { version = "0.3", features = ["stable"] }
[[bench]] [[bench]]
name = "benchmark" name = "benchmark"

View file

@ -1,18 +1,20 @@
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::ffi::{CStr, CString};
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::string::String as StdString; use std::string::String as StdString;
use bstr::{BStr, BString};
use num_traits::cast; use num_traits::cast;
use error::{Error, Result}; use crate::error::{Error, Result};
use function::Function; use crate::function::Function;
use lua::Lua; use crate::lua::Lua;
use string::String; use crate::string::String;
use table::Table; use crate::table::Table;
use thread::Thread; use crate::thread::Thread;
use types::{LightUserData, Number}; use crate::types::{LightUserData, Number};
use userdata::{AnyUserData, UserData}; use crate::userdata::{AnyUserData, UserData};
use value::{FromLua, Nil, ToLua, Value}; use crate::value::{FromLua, Nil, ToLua, Value};
impl<'lua> ToLua<'lua> for Value<'lua> { impl<'lua> ToLua<'lua> for Value<'lua> {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
@ -35,7 +37,7 @@ impl<'lua> ToLua<'lua> for String<'lua> {
impl<'lua> FromLua<'lua> for String<'lua> { impl<'lua> FromLua<'lua> for String<'lua> {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
let ty = value.type_name(); let ty = value.type_name();
lua.coerce_string(value) lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError { .ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: ty,
to: "String", to: "String",
@ -150,7 +152,7 @@ impl<'lua> FromLua<'lua> for Error {
match value { match value {
Value::Error(err) => Ok(err), Value::Error(err) => Ok(err),
val => Ok(Error::RuntimeError( val => Ok(Error::RuntimeError(
lua.coerce_string(val) lua.coerce_string(val)?
.and_then(|s| Some(s.to_str().ok()?.to_owned())) .and_then(|s| Some(s.to_str().ok()?.to_owned()))
.unwrap_or_else(|| "<unprintable error>".to_owned()), .unwrap_or_else(|| "<unprintable error>".to_owned()),
)), )),
@ -203,12 +205,13 @@ impl<'lua> FromLua<'lua> for StdString {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); let ty = value.type_name();
Ok(lua Ok(lua
.coerce_string(value) .coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError { .ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: ty,
to: "String", to: "String",
message: Some("expected string or number".to_string()), message: Some("expected string or number".to_string()),
})?.to_str()? })?
.to_str()?
.to_owned()) .to_owned())
} }
} }
@ -219,6 +222,68 @@ impl<'lua, 'a> ToLua<'lua> for &'a str {
} }
} }
impl<'lua> ToLua<'lua> for CString {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> FromLua<'lua> for CString {
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,
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()),
}),
}
}
}
impl<'lua, 'a> ToLua<'lua> for &'a CStr {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua, 'a> ToLua<'lua> for BString {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
impl<'lua> FromLua<'lua> for BString {
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(),
))
}
}
impl<'lua, 'a> ToLua<'lua> for &BStr {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
macro_rules! lua_convert_int { macro_rules! lua_convert_int {
($x:ty) => { ($x:ty) => {
impl<'lua> ToLua<'lua> for $x { impl<'lua> ToLua<'lua> for $x {
@ -231,7 +296,8 @@ macro_rules! lua_convert_int {
from: stringify!($x), from: stringify!($x),
to: "number", to: "number",
message: Some("out of range".to_owned()), message: Some("out of range".to_owned()),
}).map(Value::Number) })
.map(Value::Number)
} }
} }
} }
@ -239,20 +305,20 @@ macro_rules! lua_convert_int {
impl<'lua> FromLua<'lua> for $x { impl<'lua> FromLua<'lua> for $x {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); let ty = value.type_name();
(if let Some(i) = lua.coerce_integer(value.clone()) { (if let Some(i) = lua.coerce_integer(value.clone())? {
cast(i) cast(i)
} else { } else {
cast( cast(lua.coerce_number(value)?.ok_or_else(|| {
lua.coerce_number(value) Error::FromLuaConversionError {
.ok_or_else(|| Error::FromLuaConversionError { from: ty,
from: ty, to: stringify!($x),
to: stringify!($x), message: Some(
message: Some( "expected number or string coercible to number".to_string(),
"expected number or string coercible to number".to_string(), ),
), }
})?, })?)
) })
}).ok_or_else(|| Error::FromLuaConversionError { .ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: ty,
to: stringify!($x), to: stringify!($x),
message: Some("out of range".to_owned()), message: Some("out of range".to_owned()),
@ -286,12 +352,13 @@ macro_rules! lua_convert_float {
impl<'lua> FromLua<'lua> for $x { impl<'lua> FromLua<'lua> for $x {
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name(); let ty = value.type_name();
lua.coerce_number(value) lua.coerce_number(value)?
.ok_or_else(|| Error::FromLuaConversionError { .ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: ty,
to: stringify!($x), to: stringify!($x),
message: Some("expected number or string coercible to number".to_string()), message: Some("expected number or string coercible to number".to_string()),
}).and_then(|n| { })
.and_then(|n| {
cast(n).ok_or_else(|| Error::FromLuaConversionError { cast(n).ok_or_else(|| Error::FromLuaConversionError {
from: ty, from: ty,
to: stringify!($x), to: stringify!($x),

View file

@ -1,16 +1,16 @@
use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use std::string::String as StdString;
use std::sync::Arc; use std::sync::Arc;
use failure;
/// Error type returned by `rlua` methods. /// Error type returned by `rlua` methods.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Error { pub enum Error {
/// Syntax error while parsing Lua source code. /// Syntax error while parsing Lua source code.
SyntaxError { SyntaxError {
/// The error message as returned by Lua. /// The error message as returned by Lua.
message: String, message: StdString,
/// `true` if the error can likely be fixed by appending more input to the source code. /// `true` if the error can likely be fixed by appending more input to the source code.
/// ///
/// This is useful for implementing REPLs as they can query the user for more input if this /// This is useful for implementing REPLs as they can query the user for more input if this
@ -22,11 +22,16 @@ pub enum Error {
/// The Lua VM returns this error when a builtin operation is performed on incompatible types. /// The Lua VM returns this error when a builtin operation is performed on incompatible types.
/// Among other things, this includes invoking operators on wrong types (such as calling or /// Among other things, this includes invoking operators on wrong types (such as calling or
/// indexing a `nil` value). /// indexing a `nil` value).
RuntimeError(String), RuntimeError(StdString),
/// Lua memory error, aka `LUA_ERRMEM`
///
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
/// it is an out-of-memory error.
MemoryError(StdString),
/// Lua garbage collector error, aka `LUA_ERRGCMM`. /// Lua garbage collector error, aka `LUA_ERRGCMM`.
/// ///
/// The Lua VM returns this error when there is an error running a `__gc` metamethod. /// The Lua VM returns this error when there is an error running a `__gc` metamethod.
GarbageCollectorError(String), GarbageCollectorError(StdString),
/// A mutable callback has triggered Lua code that has called the same mutable callback again. /// A mutable callback has triggered Lua code that has called the same mutable callback again.
/// ///
/// This is an error because a mutable callback can only be borrowed mutably once. /// This is an error because a mutable callback can only be borrowed mutably once.
@ -53,7 +58,7 @@ pub enum Error {
/// Name of the Lua type that could not be created. /// Name of the Lua type that could not be created.
to: &'static str, to: &'static str,
/// A message indicating why the conversion failed in more detail. /// A message indicating why the conversion failed in more detail.
message: Option<String>, message: Option<StdString>,
}, },
/// A Lua value could not be converted to the expected Rust type. /// A Lua value could not be converted to the expected Rust type.
FromLuaConversionError { FromLuaConversionError {
@ -62,7 +67,7 @@ pub enum Error {
/// Name of the Rust type that could not be created. /// Name of the Rust type that could not be created.
to: &'static str, to: &'static str,
/// A string containing more detailed error information. /// A string containing more detailed error information.
message: Option<String>, message: Option<StdString>,
}, },
/// [`Thread::resume`] was called on an inactive coroutine. /// [`Thread::resume`] was called on an inactive coroutine.
/// ///
@ -107,7 +112,7 @@ pub enum Error {
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error. /// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
CallbackError { CallbackError {
/// Lua call stack backtrace. /// Lua call stack backtrace.
traceback: String, traceback: StdString,
/// Original error returned by the Rust code. /// Original error returned by the Rust code.
cause: Arc<Error>, cause: Arc<Error>,
}, },
@ -118,7 +123,7 @@ pub enum Error {
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua /// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`, /// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered. /// from which the original error (and a stack traceback) can be recovered.
ExternalError(Arc<failure::Error>), ExternalError(Arc<dyn StdError + Send + Sync>),
} }
/// A specialized `Result` type used by `rlua`'s API. /// A specialized `Result` type used by `rlua`'s API.
@ -129,6 +134,9 @@ impl fmt::Display for Error {
match *self { match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message), Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg), Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
Error::MemoryError(ref msg) => {
write!(fmt, "memory error: {}", msg)
}
Error::GarbageCollectorError(ref msg) => { Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {}", msg) write!(fmt, "garbage collector error: {}", msg)
} }
@ -174,34 +182,27 @@ impl fmt::Display for Error {
Error::MismatchedRegistryKey => { Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state") write!(fmt, "RegistryKey used from different Lua state")
} }
Error::CallbackError { ref traceback, .. } => { Error::CallbackError { ref traceback, ref cause } => {
write!(fmt, "callback error: {}", traceback) write!(fmt, "callback error: {}: {}", cause, traceback)
} }
Error::ExternalError(ref err) => err.fmt(fmt), Error::ExternalError(ref err) => write!(fmt, "external error: {}", err),
} }
} }
} }
impl failure::Fail for Error { impl StdError for Error {
fn cause(&self) -> Option<&dyn failure::Fail> { fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self { match *self {
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()), Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
Error::ExternalError(ref err) => err.as_fail().cause(), Error::ExternalError(ref err) => Some(err.as_ref()),
_ => None,
}
}
fn backtrace(&self) -> Option<&failure::Backtrace> {
match *self {
Error::ExternalError(ref err) => Some(err.backtrace()),
_ => None, _ => None,
} }
} }
} }
impl Error { impl Error {
pub fn external<T: Into<failure::Error>>(err: T) -> Error { pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
Error::ExternalError(Arc::new(err.into())) Error::ExternalError(err.into().into())
} }
} }
@ -211,7 +212,7 @@ pub trait ExternalError {
impl<E> ExternalError for E impl<E> ExternalError for E
where where
E: Into<failure::Error>, E: Into<Box<dyn StdError + Send + Sync>>,
{ {
fn to_lua_err(self) -> Error { fn to_lua_err(self) -> Error {
Error::external(self) Error::external(self)

View file

@ -23,11 +23,11 @@
//! Contains definitions from `lauxlib.h`. //! Contains definitions from `lauxlib.h`.
use libc::{c_int, c_long, c_char, c_void, size_t}; use libc::{c_int, c_long, c_char, c_void, size_t};
use ffi::lua;
use ffi::lua::{lua_State, lua_CFunction, lua_Integer, lua_Number};
use ffi::luaconf::LUAL_BUFFERSIZE;
use std::ptr; use std::ptr;
use super::lua::{self, lua_State, lua_CFunction, lua_Integer, lua_Number};
use super::luaconf::LUAL_BUFFERSIZE;
pub use super::glue::LUAL_NUMSIZES; pub use super::glue::LUAL_NUMSIZES;
pub use super::glue::LUA_FILEHANDLE; pub use super::glue::LUA_FILEHANDLE;

View file

@ -23,9 +23,10 @@
//! Contains definitions from `lua.h`. //! Contains definitions from `lua.h`.
use libc::{c_void, c_int, c_char, c_uchar, size_t}; use libc::{c_void, c_int, c_char, c_uchar, size_t};
use ffi::luaconf;
use std::ptr; use std::ptr;
use super::luaconf;
pub use super::glue::{LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_NUM, LUA_VERSION_RELEASE}; pub use super::glue::{LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_NUM, LUA_VERSION_RELEASE};
pub use super::glue::{LUA_VERSION, LUA_RELEASE, LUA_COPYRIGHT, LUA_AUTHORS}; pub use super::glue::{LUA_VERSION, LUA_RELEASE, LUA_COPYRIGHT, LUA_AUTHORS};
pub use super::glue::{LUA_REGISTRYINDEX}; pub use super::glue::{LUA_REGISTRYINDEX};

View file

@ -22,9 +22,10 @@
//! Contains definitions from `lualib.h`. //! Contains definitions from `lualib.h`.
use ffi::lua::lua_State;
use libc::c_int; use libc::c_int;
use super::lua::lua_State;
pub use super::glue::{ pub use super::glue::{
LUA_COLIBNAME, LUA_TABLIBNAME, LUA_IOLIBNAME, LUA_OSLIBNAME, LUA_STRLIBNAME, LUA_COLIBNAME, LUA_TABLIBNAME, LUA_IOLIBNAME, LUA_OSLIBNAME, LUA_STRLIBNAME,
LUA_UTF8LIBNAME, LUA_BITLIBNAME, LUA_MATHLIBNAME, LUA_DBLIBNAME, LUA_LOADLIBNAME LUA_UTF8LIBNAME, LUA_BITLIBNAME, LUA_MATHLIBNAME, LUA_DBLIBNAME, LUA_LOADLIBNAME

View file

@ -1,13 +1,13 @@
use std::os::raw::c_int; use std::os::raw::c_int;
use std::ptr; use std::ptr;
use error::{Error, Result}; use crate::error::{Error, Result};
use ffi; use crate::ffi;
use types::LuaRef; use crate::types::LuaRef;
use util::{ use crate::util::{
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard, assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
}; };
use value::{FromLuaMulti, MultiValue, ToLuaMulti}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
/// Handle to an internal Lua function. /// Handle to an internal Lua function.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -23,9 +23,8 @@ impl<'lua> Function<'lua> {
/// Call Lua's built-in `tostring` function: /// Call Lua's built-in `tostring` function:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Function, Result}; /// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let globals = lua.globals(); /// let globals = lua.globals();
/// ///
@ -35,32 +34,26 @@ impl<'lua> Function<'lua> {
/// ///
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// Call a function with multiple arguments: /// Call a function with multiple arguments:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Function, Result}; /// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// ///
/// let sum: Function = lua.eval(r#" /// let sum: Function = lua.load(
/// function(a, b) /// r#"
/// return a + b /// function(a, b)
/// end /// return a + b
/// "#, None)?; /// end
/// "#).eval()?;
/// ///
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4); /// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
/// ///
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> { pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua; let lua = self.0.lua;
@ -76,7 +69,7 @@ impl<'lua> Function<'lua> {
let stack_start = ffi::lua_gettop(lua.state); let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(&self.0); lua.push_ref(&self.0);
for arg in args { for arg in args {
lua.push_value(arg); lua.push_value(arg)?;
} }
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start); let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK { if ret != ffi::LUA_OK {
@ -102,16 +95,15 @@ impl<'lua> Function<'lua> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Function, Result}; /// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// ///
/// let sum: Function = lua.eval(r#" /// let sum: Function = lua_context.load(r#"
/// function(a, b) /// function(a, b)
/// return a + b /// return a + b
/// end /// end
/// "#, None)?; /// "#).eval()?;
/// ///
/// let bound_a = sum.bind(1)?; /// let bound_a = sum.bind(1)?;
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2); /// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
@ -121,9 +113,6 @@ impl<'lua> Function<'lua> {
/// ///
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> { pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
@ -161,7 +150,7 @@ impl<'lua> Function<'lua> {
lua.push_ref(&self.0); lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer); ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args { for arg in args {
lua.push_value(arg); lua.push_value(arg)?;
} }
protect_lua_closure(lua.state, nargs + 2, 1, |state| { protect_lua_closure(lua.state, nargs + 2, 1, |state| {

View file

@ -40,15 +40,12 @@
// warnings at all. // warnings at all.
#![doc(test(attr(deny(warnings))))] #![doc(test(attr(deny(warnings))))]
extern crate failure;
extern crate libc;
extern crate num_traits;
mod error;
mod ffi;
#[macro_use] #[macro_use]
mod macros; mod macros;
mod conversion; mod conversion;
mod error;
mod ffi;
mod function; mod function;
mod lua; mod lua;
mod multi; mod multi;
@ -61,18 +58,18 @@ mod userdata;
mod util; mod util;
mod value; mod value;
pub use error::{Error, ExternalError, ExternalResult, Result}; pub use crate::ffi::lua_State;
pub use function::Function;
pub use lua::Lua;
pub use multi::Variadic;
pub use scope::Scope;
pub use string::String;
pub use table::{Table, TablePairs, TableSequence};
pub use thread::{Thread, ThreadStatus};
pub use types::{Integer, LightUserData, Number, RegistryKey};
pub use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
pub use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
pub use ffi::lua_State; pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::Function;
pub use crate::lua::{Lua, Chunk};
pub use crate::multi::Variadic;
pub use crate::scope::Scope;
pub use crate::string::String;
pub use crate::table::{Table, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
pub mod prelude; pub mod prelude;

View file

@ -4,35 +4,33 @@ use std::collections::HashMap;
use std::ffi::CString; use std::ffi::CString;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::string::String as StdString;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{mem, ptr, str}; use std::{mem, ptr, str};
use libc; use crate::error::{Error, Result};
use crate::ffi;
use error::{Error, Result}; use crate::function::Function;
use ffi; use crate::scope::Scope;
use function::Function; use crate::string::String;
use scope::Scope; use crate::table::Table;
use string::String; use crate::thread::Thread;
use table::Table; use crate::types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey};
use thread::Thread; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey}; use crate::util::{
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; assert_stack, callback_error, check_stack, get_userdata, get_wrapped_error,
use util::{ init_error_registry, init_userdata_metatable, main_state, pop_error,
assert_stack, callback_error, check_stack, gc_guard, get_userdata, get_wrapped_error, protect_lua, protect_lua_closure, safe_pcall, safe_xpcall,
init_error_metatables, init_userdata_metatable, main_state, pop_error, protect_lua, push_string, push_userdata, push_wrapped_error, StackGuard,
protect_lua_closure, push_string, push_userdata, push_wrapped_error, safe_pcall, safe_xpcall, userdata_destructor,
userdata_destructor, StackGuard,
}; };
use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
/// Top level Lua struct which holds the Lua state itself. /// Top level Lua struct which holds the Lua state itself.
pub struct Lua { pub struct Lua {
pub(crate) state: *mut ffi::lua_State, pub(crate) state: *mut ffi::lua_State,
main_state: *mut ffi::lua_State, main_state: *mut ffi::lua_State,
// Lua has lots of interior mutability, should not be RefUnwindSafe // Lua has lots of interior mutability, should not be RefUnwindSafe
_phantom: PhantomData<UnsafeCell<()>>, _no_ref_unwind_safe: PhantomData<UnsafeCell<()>>,
} }
unsafe impl Send for Lua {} unsafe impl Send for Lua {}
@ -42,33 +40,55 @@ impl Lua {
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua { pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
let state_top = ffi::lua_gettop(state); let state_top = ffi::lua_gettop(state);
init_error_metatables(state); let ref_thread = rlua_expect!(
protect_lua_closure(state, 0, 0, |state| {
init_error_registry(state);
// Create the function metatable // Create the function metatable
ffi::lua_pushlightuserdata( ffi::lua_pushlightuserdata(
state, state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
ffi::lua_newtable(state);
ffi::lua_pushstring(state, cstr!("__gc"));
ffi::lua_pushcfunction(state, userdata_destructor::<Callback>);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("__metatable"));
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
// Override pcall and xpcall with versions that cannot be used to catch rust panics.
/*
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
ffi::lua_pushstring(state, cstr!("pcall"));
ffi::lua_pushcfunction(state, safe_pcall);
ffi::lua_rawset(state, -3);
ffi::lua_pushstring(state, cstr!("xpcall"));
ffi::lua_pushcfunction(state, safe_xpcall);
ffi::lua_rawset(state, -3);
ffi::lua_pop(state, 1);
*/
// Create ref stack thread and place it in the registry to prevent it from being garbage
// collected.
let _ref_thread = ffi::lua_newthread(state);
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
_ref_thread
}),
"Error during Lua construction",
); );
ffi::lua_newtable(state);
push_string(state, "__gc").unwrap();
ffi::lua_pushcfunction(state, userdata_destructor::<Callback>);
ffi::lua_rawset(state, -3);
push_string(state, "__metatable").unwrap();
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
// Create ref stack thread and place it in the registry to prevent it from being garbage
// collected.
let ref_thread = ffi::lua_newthread(state);
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
// Create ExtraData, and place it in the lua_State "extra space" // Create ExtraData, and place it in the lua_State "extra space"
let extra = Box::into_raw(Box::new(ExtraData { let extra = Box::into_raw(Box::new(ExtraData {
@ -80,97 +100,148 @@ impl Lua {
ref_stack_max: 0, ref_stack_max: 0,
ref_free: Vec::new(), ref_free: Vec::new(),
})); }));
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra;
rlua_debug_assert!(ffi::lua_gettop(state) == state_top, "stack leak during creation"); rlua_debug_assert!(ffi::lua_gettop(state) == state_top, "stack leak during creation");
assert_stack(state, ffi::LUA_MINSTACK); assert_stack(state, ffi::LUA_MINSTACK);
// Place pointer to ExtraData in the lua_State "extra space"
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra;
Lua { Lua {
state, state,
main_state: main_state(state), main_state: main_state(state),
_phantom: PhantomData, _no_ref_unwind_safe: PhantomData,
} }
} }
/// Loads a chunk of Lua code and returns it as a function. /// Returns true if the garbage collector is currently running automatically.
pub fn gc_is_running(&self) -> bool {
unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCISRUNNING, 0) != 0 }
}
/// Stop the Lua GC from running
pub fn gc_stop(&self) {
unsafe {
ffi::lua_gc(self.main_state, ffi::LUA_GCSTOP, 0);
}
}
/// Restarts the Lua GC if it is not running
pub fn gc_restart(&self) {
unsafe {
ffi::lua_gc(self.main_state, ffi::LUA_GCRESTART, 0);
}
}
/// Perform a full garbage-collection cycle.
/// ///
/// The source can be named by setting the `name` parameter. This is generally recommended as it /// It may be necessary to call this function twice to collect all currently unreachable
/// results in better error traces. /// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
pub fn gc_collect(&self) -> Result<()> {
unsafe {
protect_lua_closure(self.main_state, 0, 0, |state| {
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
})
}
}
/// Steps the garbage collector one indivisible step.
/// ///
/// Equivalent to Lua's `load` function. /// Returns true if this has finished a collection cycle.
pub fn load<S>(&self, source: &S, name: Option<&str>) -> Result<Function> pub fn gc_step(&self) -> Result<bool> {
self.gc_step_kbytes(0)
}
/// Steps the garbage collector as though memory had been allocated.
///
/// if `kbytes` is 0, then this is the same as calling `gc_step`. Returns true if this step has
/// finished a collection cycle.
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
unsafe {
protect_lua_closure(self.main_state, 0, 0, |state| {
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
})
}
}
/// Sets the 'pause' value of the collector.
///
/// Returns the previous value of 'pause'. More information can be found in the [Lua 5.3
/// documentation][lua_doc].
///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCSETPAUSE, pause) }
}
/// Sets the 'step multiplier' value of the collector.
///
/// Returns the previous value of the 'step multiplier'. More information can be found in the
/// [Lua 5.3 documentation][lua_doc].
///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCSETSTEPMUL, step_multiplier) }
}
/// Returns Lua source code as a `Chunk` builder type.
///
/// In order to actually compile or run the resulting code, you must call [`Chunk::exec`] or
/// similar on the returned builder. Code is not even parsed until one of these methods is
/// called.
///
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
where where
S: ?Sized + AsRef<[u8]>, S: ?Sized + AsRef<[u8]>,
{ {
Chunk {
lua: self,
source: source.as_ref(),
name: None,
env: None,
}
}
fn load_chunk<'lua>(
&'lua self,
source: &[u8],
name: Option<&CString>,
env: Option<Value<'lua>>
) -> Result<Function<'lua>> {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1); assert_stack(self.state, 1);
let source = source.as_ref();
match if let Some(name) = name { match if let Some(name) = name {
let name = ffi::luaL_loadbufferx(
CString::new(name.to_owned()).map_err(|e| Error::ToLuaConversionError {
from: "&str",
to: "string",
message: Some(e.to_string()),
})?;
ffi::luaL_loadbuffer(
self.state, self.state,
source.as_ptr() as *const c_char, source.as_ptr() as *const c_char,
source.len(), source.len(),
name.as_ptr(), name.as_ptr() as *const c_char,
cstr!("t"),
) )
} else { } else {
ffi::luaL_loadbuffer( ffi::luaL_loadbufferx(
self.state, self.state,
source.as_ptr() as *const c_char, source.as_ptr() as *const c_char,
source.len(), source.len(),
ptr::null(), ptr::null(),
cstr!("t"),
) )
} { } {
ffi::LUA_OK => Ok(Function(self.pop_ref())), ffi::LUA_OK => {
if let Some(env) = env {
self.push_value(env)?;
ffi::lua_setupvalue(self.state, -2, 1);
}
Ok(Function(self.pop_ref()))
}
err => Err(pop_error(self.state, err)), err => Err(pop_error(self.state, err)),
} }
} }
} }
/// Execute a chunk of Lua code.
///
/// This is equivalent to simply loading the source with `load` and then calling the resulting
/// function with no arguments.
///
/// Returns the values returned by the chunk.
pub fn exec<'lua, S, R: FromLuaMulti<'lua>>(
&'lua self,
source: &S,
name: Option<&str>,
) -> Result<R>
where
S: ?Sized + AsRef<[u8]>,
R: FromLuaMulti<'lua>,
{
self.load(source, name)?.call(())
}
/// Evaluate the given expression or chunk inside this Lua state.
///
/// If `source` is an expression, returns the value it evaluates to. Otherwise, returns the
/// values returned by the chunk (if any).
pub fn eval<'lua, S, R>(&'lua self, source: &S, name: Option<&str>) -> Result<R>
where
S: ?Sized + AsRef<[u8]>,
R: FromLuaMulti<'lua>,
{
// First, try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
// actual lua repl does.
let mut return_source = "return ".as_bytes().to_vec();
return_source.extend(source.as_ref());
self.load(&return_source, name)
.or_else(|_| self.load(source, name))?
.call(())
}
/// Create and return an interned Lua string. Lua strings can be arbitrary [u8] data including /// Create and return an interned Lua string. Lua strings can be arbitrary [u8] data including
/// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]` /// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]`
/// here. /// here.
@ -220,8 +291,8 @@ impl Lua {
protect_lua(self.state, 0, new_table)?; protect_lua(self.state, 0, new_table)?;
for (k, v) in cont { for (k, v) in cont {
self.push_value(k.to_lua(self)?); self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?); self.push_value(v.to_lua(self)?)?;
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
1 1
@ -257,9 +328,8 @@ impl Lua {
/// Create a function which prints its argument: /// Create a function which prints its argument:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result}; /// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// ///
/// let greet = lua.create_function(|_, name: String| { /// let greet = lua.create_function(|_, name: String| {
@ -269,17 +339,13 @@ impl Lua {
/// # let _ = greet; // used /// # let _ = greet; // used
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// Use tuples to accept multiple arguments: /// Use tuples to accept multiple arguments:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result}; /// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// ///
/// let print_person = lua.create_function(|_, (name, age): (String, u8)| { /// let print_person = lua.create_function(|_, (name, age): (String, u8)| {
@ -289,9 +355,6 @@ impl Lua {
/// # let _ = print_person; // used /// # let _ = print_person; // used
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`ToLua`]: trait.ToLua.html /// [`ToLua`]: trait.ToLua.html
@ -365,6 +428,15 @@ impl Lua {
} }
} }
/// Returns a handle to the active `Thread`. For calls to `Lua` this will be the main Lua thread,
/// for parameters given to a callback, this will be whatever Lua thread called the callback.
pub fn current_thread<'lua>(&'lua self) -> Thread<'lua> {
unsafe {
ffi::lua_pushthread(self.state);
Thread(self.pop_ref())
}
}
/// Calls the given function with a `Scope` parameter, giving the function the ability to create /// Calls the given function with a `Scope` parameter, giving the function the ability to create
/// userdata and callbacks from rust types that are !Send or non-'static. /// userdata and callbacks from rust types that are !Send or non-'static.
/// ///
@ -376,20 +448,20 @@ impl Lua {
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only /// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
/// outlive the scope lifetime. /// outlive the scope lifetime.
/// ///
/// Handles that `Lua::scope` produces have a `'lua` lifetime of the scope parameter, to prevent /// Inside the scope callback, all handles created through Scope will share the same unique 'lua
/// the handles from escaping the callback. However, this is not the only way for values to /// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
/// escape the callback, as they can be smuggled through Lua itself. This is safe to do, but /// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
/// not very useful, because after the scope is dropped, all references to scoped values, /// However, this also enables handles to scoped values to be trivially leaked from the given
/// whether in Lua or in rust, are invalidated. `Function` types will error when called, and /// callback. This is not dangerous, though! After the callback returns, all scoped values are
/// `AnyUserData` types will be typeless. /// invalidated, which means that though references may exist, the Rust types backing them have
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
/// would always be able to smuggle them through Lua state.
pub fn scope<'scope, 'lua: 'scope, F, R>(&'lua self, f: F) -> R pub fn scope<'scope, 'lua: 'scope, F, R>(&'lua self, f: F) -> R
where where
F: FnOnce(&Scope<'scope>) -> R, F: FnOnce(&Scope<'lua, 'scope>) -> R,
{ {
let scope = Scope::new(self); f(&Scope::new(self))
let r = f(&scope);
drop(scope);
r
} }
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal /// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
@ -397,22 +469,23 @@ impl Lua {
/// ///
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a /// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
/// number. /// number.
pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Option<String<'lua>> { pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Result<Option<String<'lua>>> {
match v { Ok(match v {
Value::String(s) => Some(s), Value::String(s) => Some(s),
v => unsafe { v => unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4); assert_stack(self.state, 4);
self.push_value(v); self.push_value(v)?;
let s = gc_guard(self.state, || ffi::lua_tostring(self.state, -1)); if protect_lua_closure(self.state, 1, 1, |state| {
if s.is_null() { !ffi::lua_tostring(state, -1).is_null()
None })? {
} else {
Some(String(self.pop_ref())) Some(String(self.pop_ref()))
} else {
None
} }
}, },
} })
} }
/// Attempts to coerce a Lua value into an integer in a manner consistent with Lua's internal /// Attempts to coerce a Lua value into an integer in a manner consistent with Lua's internal
@ -421,14 +494,14 @@ impl Lua {
/// To succeed, the value must be an integer, a floating point number that has an exact /// To succeed, the value must be an integer, a floating point number that has an exact
/// representation as an integer, or a string that can be converted to an integer. Refer to the /// representation as an integer, or a string that can be converted to an integer. Refer to the
/// Lua manual for details. /// Lua manual for details.
pub fn coerce_integer(&self, v: Value) -> Option<Integer> { pub fn coerce_integer(&self, v: Value) -> Result<Option<Integer>> {
match v { Ok(match v {
Value::Integer(i) => Some(i), Value::Integer(i) => Some(i),
v => unsafe { v => unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); assert_stack(self.state, 2);
self.push_value(v); self.push_value(v)?;
let mut isint = 0; let mut isint = 0;
let i = ffi::lua_tointegerx(self.state, -1, &mut isint); let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
if isint == 0 { if isint == 0 {
@ -437,7 +510,7 @@ impl Lua {
Some(i) Some(i)
} }
}, },
} })
} }
/// Attempts to coerce a Lua value into a Number in a manner consistent with Lua's internal /// Attempts to coerce a Lua value into a Number in a manner consistent with Lua's internal
@ -445,14 +518,14 @@ impl Lua {
/// ///
/// To succeed, the value must be a number or a string that can be converted to a number. Refer /// To succeed, the value must be a number or a string that can be converted to a number. Refer
/// to the Lua manual for details. /// to the Lua manual for details.
pub fn coerce_number(&self, v: Value) -> Option<Number> { pub fn coerce_number(&self, v: Value) -> Result<Option<Number>> {
match v { Ok(match v {
Value::Number(n) => Some(n), Value::Number(n) => Some(n),
v => unsafe { v => unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); assert_stack(self.state, 2);
self.push_value(v); self.push_value(v)?;
let mut isnum = 0; let mut isnum = 0;
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum); let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
if isnum == 0 { if isnum == 0 {
@ -461,7 +534,7 @@ impl Lua {
Some(n) Some(n)
} }
}, },
} })
} }
/// Converts a value that implements `ToLua` into a `Value` instance. /// Converts a value that implements `ToLua` into a `Value` instance.
@ -491,18 +564,22 @@ impl Lua {
/// ///
/// This value will be available to rust from all `Lua` instances which share the same main /// This value will be available to rust from all `Lua` instances which share the same main
/// state. /// state.
pub fn set_named_registry_value<'lua, T: ToLua<'lua>>( pub fn set_named_registry_value<'lua, S, T>(
&'lua self, &'lua self,
name: &str, name: &S,
t: T, t: T,
) -> Result<()> { ) -> Result<()>
where
S: ?Sized + AsRef<[u8]>,
T: ToLua<'lua>,
{
let t = t.to_lua(self)?; let t = t.to_lua(self)?;
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 5); assert_stack(self.state, 5);
push_string(self.state, name)?; push_string(self.state, name)?;
self.push_value(t); self.push_value(t)?;
unsafe extern "C" fn set_registry(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn set_registry(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
@ -518,7 +595,11 @@ impl Lua {
/// get a value previously set by [`set_named_registry_value`]. /// get a value previously set by [`set_named_registry_value`].
/// ///
/// [`set_named_registry_value`]: #method.set_named_registry_value /// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn named_registry_value<'lua, T: FromLua<'lua>>(&'lua self, name: &str) -> Result<T> { pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result<T>
where
S: ?Sized + AsRef<[u8]>,
T: FromLua<'lua>,
{
let value = unsafe { let value = unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4); assert_stack(self.state, 4);
@ -540,7 +621,10 @@ impl Lua {
/// Equivalent to calling [`set_named_registry_value`] with a value of Nil. /// Equivalent to calling [`set_named_registry_value`] with a value of Nil.
/// ///
/// [`set_named_registry_value`]: #method.set_named_registry_value /// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn unset_named_registry_value<'lua>(&'lua self, name: &str) -> Result<()> { pub fn unset_named_registry_value<'lua, S>(&'lua self, name: &S) -> Result<()>
where
S: ?Sized + AsRef<[u8]>,
{
self.set_named_registry_value(name, Nil) self.set_named_registry_value(name, Nil)
} }
@ -548,16 +632,21 @@ impl Lua {
/// ///
/// This value will be available to rust from all `Lua` instances which share the same main /// This value will be available to rust from all `Lua` instances which share the same main
/// state. /// state.
///
/// Be warned, garbage collection of values held inside the registry is not automatic, see
/// [`RegistryKey`] for more details.
///
/// [`RegistryKey`]: struct.RegistryKey.html
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> { pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
let t = t.to_lua(self)?; let t = t.to_lua(self)?;
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); assert_stack(self.state, 2);
self.push_value(t); self.push_value(t)?;
let registry_id = gc_guard(self.state, || { let registry_id = protect_lua_closure(self.state, 1, 0, |state| {
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX) ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
}); })?;
Ok(RegistryKey { Ok(RegistryKey {
registry_id, registry_id,
@ -634,13 +723,13 @@ impl Lua {
pub fn expire_registry_values(&self) { pub fn expire_registry_values(&self) {
unsafe { unsafe {
let unref_list = mem::replace( let unref_list = mem::replace(
&mut *(*extra_data(self.state)) &mut *rlua_expect!(
.registry_unref_list (*extra_data(self.state)).registry_unref_list.lock(),
.lock() "unref list poisoned"
.unwrap(), ),
Some(Vec::new()), Some(Vec::new()),
); );
for id in unref_list.unwrap() { for id in rlua_expect!(unref_list, "unref list not set") {
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id); ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id);
} }
} }
@ -648,7 +737,7 @@ impl Lua {
// Uses 2 stack spaces, does not call checkstack // Uses 2 stack spaces, does not call checkstack
// TODO: return to original // TODO: return to original
pub unsafe fn push_value(&self, value: Value) { pub unsafe fn push_value(&self, value: Value) -> Result<()> {
match value { match value {
Value::Nil => { Value::Nil => {
ffi::lua_pushnil(self.state); ffi::lua_pushnil(self.state);
@ -691,9 +780,11 @@ impl Lua {
} }
Value::Error(e) => { Value::Error(e) => {
push_wrapped_error(self.state, e); push_wrapped_error(self.state, e)?;
} }
} }
Ok(())
} }
// Uses 2 stack spaces, does not call checkstack // Uses 2 stack spaces, does not call checkstack
@ -717,15 +808,17 @@ impl Lua {
ud ud
} }
ffi::LUA_TNUMBER => if ffi::lua_isinteger(self.state, -1) != 0 { ffi::LUA_TNUMBER => {
let i = Value::Integer(ffi::lua_tointeger(self.state, -1)); if ffi::lua_isinteger(self.state, -1) != 0 {
ffi::lua_pop(self.state, 1); let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
i ffi::lua_pop(self.state, 1);
} else { i
let n = Value::Number(ffi::lua_tonumber(self.state, -1)); } else {
ffi::lua_pop(self.state, 1); let n = Value::Number(ffi::lua_tonumber(self.state, -1));
n ffi::lua_pop(self.state, 1);
}, n
}
}
ffi::LUA_TSTRING => Value::String(String(self.pop_ref())), ffi::LUA_TSTRING => Value::String(String(self.pop_ref())),
@ -766,7 +859,7 @@ impl Lua {
// Pops the topmost element of the stack and stores a reference to it. This pins the object, // 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. // preventing garbage collection until the returned `LuaRef` is dropped.
// //
// References are stored in the stack of a specially created auxillary thread that exists only // 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 // 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 // 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 // used stack. The implementation is somewhat biased towards the use case of a relatively small
@ -816,7 +909,7 @@ impl Lua {
})?; })?;
for (k, m) in methods.meta_methods { for (k, m) in methods.meta_methods {
push_string(self.state, k.name())?; push_string(self.state, k.name())?;
self.push_value(Value::Function(self.create_callback(m)?)); self.push_value(Value::Function(self.create_callback(m)?))?;
protect_lua_closure(self.state, 3, 1, |state| { protect_lua_closure(self.state, 3, 1, |state| {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
@ -831,7 +924,7 @@ impl Lua {
})?; })?;
for (k, m) in methods.methods { for (k, m) in methods.methods {
push_string(self.state, &k)?; push_string(self.state, &k)?;
self.push_value(Value::Function(self.create_callback(m)?)); self.push_value(Value::Function(self.create_callback(m)?))?;
protect_lua_closure(self.state, 3, 1, |state| { protect_lua_closure(self.state, 3, 1, |state| {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
})?; })?;
@ -841,9 +934,9 @@ impl Lua {
ffi::lua_pop(self.state, 1); ffi::lua_pop(self.state, 1);
} }
let id = gc_guard(self.state, || { let id = protect_lua_closure(self.state, 1, 0, |state| {
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX) ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
}); })?;
(*extra_data(self.state)) (*extra_data(self.state))
.registered_userdata .registered_userdata
.insert(TypeId::of::<T>(), id); .insert(TypeId::of::<T>(), id);
@ -863,12 +956,11 @@ impl Lua {
func: Callback<'callback, 'static>, func: Callback<'callback, 'static>,
) -> Result<Function<'lua>> { ) -> Result<Function<'lua>> {
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || { callback_error(state, |nargs| {
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL { if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
return Err(Error::CallbackDestructed); return Err(Error::CallbackDestructed);
} }
let nargs = ffi::lua_gettop(state);
if nargs < ffi::LUA_MINSTACK { if nargs < ffi::LUA_MINSTACK {
check_stack(state, ffi::LUA_MINSTACK - nargs)?; check_stack(state, ffi::LUA_MINSTACK - nargs)?;
} }
@ -876,7 +968,7 @@ impl Lua {
let lua = Lua { let lua = Lua {
state: state, state: state,
main_state: main_state(state), main_state: main_state(state),
_phantom: PhantomData, _no_ref_unwind_safe: PhantomData,
}; };
let mut args = MultiValue::new(); let mut args = MultiValue::new();
@ -892,7 +984,7 @@ impl Lua {
check_stack(state, nresults)?; check_stack(state, nresults)?;
for r in results { for r in results {
lua.push_value(r); lua.push_value(r)?;
} }
Ok(nresults) Ok(nresults)
@ -957,6 +1049,89 @@ unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData) *(ffi::lua_getextraspace(state) as *mut *mut ExtraData)
} }
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
///
/// [`Lua::load`]: struct.Lua.html#method.load
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
pub struct Chunk<'lua, 'a> {
lua: &'lua Lua,
source: &'a [u8],
name: Option<CString>,
env: Option<Value<'lua>>,
}
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name<S: ?Sized + AsRef<[u8]>>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
let name =
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
from: "&str",
to: "string",
message: Some(e.to_string()),
})?;
self.name = Some(name);
Ok(self)
}
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
///
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
/// variable inside the chunk. By default this value is set to the global environment.
///
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
/// the chunk will refer to the given environment rather than the global one.
///
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
/// necessary to populate the environment in order for scripts using custom environments to be
/// useful.
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Chunk<'lua, 'a>> {
self.env = Some(env.to_lua(self.lua)?);
Ok(self)
}
/// Execute this chunk of code.
///
/// This is equivalent to calling the chunk function with no arguments and no return values.
pub fn exec(self) -> Result<()> {
self.call(())?;
Ok(())
}
/// Evaluate the chunk as either an expression or block.
///
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
/// and this is equivalent to calling `exec`.
pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> {
// First, try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
// actual lua repl does.
let mut expression_source = b"return ".to_vec();
expression_source.extend(self.source);
if let Ok(function) =
self.lua.load_chunk(&expression_source, self.name.as_ref(), self.env.clone())
{
function.call(())
} else {
self.call(())
}
}
/// Load the chunk function and call it with the given arguemnts.
///
/// This is equivalent to `into_function` and calling the resulting function.
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
self.into_function()?.call(args)
}
/// Load this chunk into a regular `Function`.
///
/// This simply compiles the chunk without actually executing it.
pub fn into_function(self) -> Result<Function<'lua>> {
self.lua.load_chunk(self.source, self.name.as_ref(), self.env)
}
}
unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int { unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
if let Some(free) = (*extra).ref_free.pop() { if let Some(free) = (*extra).ref_free.pop() {
ffi::lua_replace((*extra).ref_thread, free); ffi::lua_replace((*extra).ref_thread, free);
@ -966,7 +1141,7 @@ unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
// It is a user error to create enough references to exhaust the Lua max stack size for // It is a user error to create enough references to exhaust the Lua max stack size for
// the ref thread. // the ref thread.
if ffi::lua_checkstack((*extra).ref_thread, (*extra).ref_stack_size) == 0 { if ffi::lua_checkstack((*extra).ref_thread, (*extra).ref_stack_size) == 0 {
panic!("cannot create a Lua reference, out of auxillary stack space"); rlua_panic!("cannot create a Lua reference, out of auxiliary stack space");
} }
(*extra).ref_stack_size *= 2; (*extra).ref_stack_size *= 2;
} }
@ -978,60 +1153,64 @@ unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0; static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
struct StaticUserDataMethods<'lua, T: 'static + UserData> { struct StaticUserDataMethods<'lua, T: 'static + UserData> {
methods: HashMap<StdString, Callback<'lua, 'static>>, methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
meta_methods: HashMap<MetaMethod, Callback<'lua, 'static>>, meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
_type: PhantomData<T>, _type: PhantomData<T>,
} }
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> { impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
fn default() -> StaticUserDataMethods<'lua, T> { fn default() -> StaticUserDataMethods<'lua, T> {
StaticUserDataMethods { StaticUserDataMethods {
methods: HashMap::new(), methods: Vec::new(),
meta_methods: HashMap::new(), meta_methods: Vec::new(),
_type: PhantomData, _type: PhantomData,
} }
} }
} }
impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> { impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
fn add_method<A, R, M>(&mut self, name: &str, method: M) fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>, M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{ {
self.methods self.methods
.insert(name.to_owned(), Self::box_method(method)); .push((name.as_ref().to_vec(), Self::box_method(method)));
} }
fn add_method_mut<A, R, M>(&mut self, name: &str, method: M) fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>, M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{ {
self.methods self.methods
.insert(name.to_owned(), Self::box_method_mut(method)); .push((name.as_ref().to_vec(), Self::box_method_mut(method)));
} }
fn add_function<A, R, F>(&mut self, name: &str, function: F) fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>, F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{ {
self.methods self.methods
.insert(name.to_owned(), Self::box_function(function)); .push((name.as_ref().to_vec(), Self::box_function(function)));
} }
fn add_function_mut<A, R, F>(&mut self, name: &str, function: F) fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>, F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.methods self.methods
.insert(name.to_owned(), Self::box_function_mut(function)); .push((name.as_ref().to_vec(), Self::box_function_mut(function)));
} }
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M) fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
@ -1040,7 +1219,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>, M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{ {
self.meta_methods.insert(meta, Self::box_method(method)); self.meta_methods.push((meta, Self::box_method(method)));
} }
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M) fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
@ -1049,7 +1228,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>, M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{ {
self.meta_methods.insert(meta, Self::box_method_mut(method)); self.meta_methods.push((meta, Self::box_method_mut(method)));
} }
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F) fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
@ -1058,7 +1237,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>, F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods.insert(meta, Self::box_function(function)); self.meta_methods.push((meta, Self::box_function(function)));
} }
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F) fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F)
@ -1068,7 +1247,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>, F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods self.meta_methods
.insert(meta, Self::box_function_mut(function)); .push((meta, Self::box_function_mut(function)));
} }
} }

View file

@ -1,3 +1,13 @@
macro_rules! bug_msg {
($arg:expr) => {
concat!(
"rlua internal error: ",
$arg,
" (this is a bug, please file an issue)"
)
};
}
macro_rules! cstr { macro_rules! cstr {
($s:expr) => { ($s:expr) => {
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
@ -5,62 +15,66 @@ macro_rules! cstr {
}; };
} }
macro_rules! abort {
($msg:expr) => {
{
eprintln!($msg);
::std::process::abort()
}
};
($msg:expr, $($arg:tt)+) => {
{
eprintln!($msg, $($arg)+);
::std::process::abort()
}
};
}
macro_rules! rlua_panic { macro_rules! rlua_panic {
($msg:expr) => { ($msg:expr) => {
panic!(concat!("rlua internal error: ", $msg)); panic!(bug_msg!($msg));
}; };
($msg:expr, $($arg:tt)+) => { ($msg:expr,) => {
panic!(concat!("rlua internal error: ", $msg), $($arg)+); rlua_panic!($msg);
};
($msg:expr, $($arg:expr),+) => {
panic!(bug_msg!($msg), $($arg),+);
};
($msg:expr, $($arg:expr),+,) => {
rlua_panic!($msg, $($arg),+);
}; };
} }
macro_rules! rlua_assert { macro_rules! rlua_assert {
($cond:expr, $msg:expr) => { ($cond:expr, $msg:expr) => {
assert!($cond, concat!("rlua internal error: ", $msg)); assert!($cond, bug_msg!($msg));
}; };
($cond:expr, $msg:expr, $($arg:tt)+) => { ($cond:expr, $msg:expr,) => {
assert!($cond, concat!("rlua internal error: ", $msg), $($arg)+); rlua_assert!($cond, $msg);
};
($cond:expr, $msg:expr, $($arg:expr),+) => {
assert!($cond, bug_msg!($msg), $($arg),+);
};
($cond:expr, $msg:expr, $($arg:expr),+,) => {
rlua_assert!($cond, $msg, $($arg),+);
}; };
} }
macro_rules! rlua_debug_assert { macro_rules! rlua_debug_assert {
($cond:expr, $msg:expr) => { ($cond:expr, $msg:expr) => {
debug_assert!($cond, concat!("rlua internal error: ", $msg)); debug_assert!($cond, bug_msg!($msg));
}; };
($cond:expr, $msg:expr, $($arg:tt)+) => { ($cond:expr, $msg:expr,) => {
debug_assert!($cond, concat!("rlua internal error: ", $msg), $($arg)+); rlua_debug_assert!($cond, $msg);
};
($cond:expr, $msg:expr, $($arg:expr),+) => {
debug_assert!($cond, bug_msg!($msg), $($arg),+);
};
($cond:expr, $msg:expr, $($arg:expr),+,) => {
rlua_debug_assert!($cond, $msg, $($arg),+);
}; };
} }
macro_rules! rlua_abort { macro_rules! rlua_expect {
($msg:expr) => { ($res:expr, $msg:expr) => {
{ $res.expect(bug_msg!($msg))
abort!(concat!("rlua internal error: ", $msg));
}
}; };
($msg:expr, $($arg:tt)+) => { ($res:expr, $msg:expr,) => {
{ rlua_expect!($res, $msg)
abort!(concat!("rlua internal error, aborting!: ", $msg), $($arg)+);
}
}; };
} }

View file

@ -2,9 +2,9 @@ use std::iter::FromIterator;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult; use std::result::Result as StdResult;
use error::Result; use crate::error::Result;
use lua::Lua; use crate::lua::Lua;
use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti}; use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result /// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message. /// on success, or in the case of an error, returning `nil` and an error message.
@ -62,21 +62,16 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Variadic, Result}; /// # use rlua::{Lua, Variadic, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
///
/// let add = lua.create_function(|_, vals: Variadic<f64>| -> Result<f64> { /// let add = lua.create_function(|_, vals: Variadic<f64>| -> Result<f64> {
/// Ok(vals.iter().sum()) /// Ok(vals.iter().sum())
/// }).unwrap(); /// }).unwrap();
/// lua.globals().set("add", add)?; /// lua.globals().set("add", add)?;
/// assert_eq!(lua.eval::<_, f64>("add(3, 2, 5)", None)?, 10.0); /// assert_eq!(lua.load("add(3, 2, 5)").eval::<f32>()?, 10.0);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`FromLua`]: trait.FromLua.html /// [`FromLua`]: trait.FromLua.html
@ -91,6 +86,12 @@ impl<T> Variadic<T> {
} }
} }
impl<T> Default for Variadic<T> {
fn default() -> Variadic<T> {
Variadic::new()
}
}
impl<T> FromIterator<T> for Variadic<T> { impl<T> FromIterator<T> for Variadic<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Variadic(Vec::from_iter(iter)) Variadic(Vec::from_iter(iter))

View file

@ -1,8 +1,9 @@
//! Re-exports most types with an extra `Lua*` prefix to prevent name clashes. //! Re-exports most types with an extra `Lua*` prefix to prevent name clashes.
pub use { pub use crate::{
AnyUserData as LuaAnyUserData, Error as LuaError, ExternalError as LuaExternalError, AnyUserData as LuaAnyUserData, Chunk as LuaChunk,
ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, Function as LuaFunction, Error as LuaError, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
FromLua, FromLuaMulti, Function as LuaFunction,
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, Scope as LuaScope, String as LuaString, Table as LuaTable, Result as LuaResult, Scope as LuaScope, String as LuaString, Table as LuaTable,

View file

@ -1,67 +1,65 @@
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::rc::Rc; use std::rc::Rc;
use std::string::String as StdString; use std::cell::Cell;
use error::{Error, Result}; use crate::error::{Error, Result};
use ffi; use crate::lua::Lua;
use function::Function; use crate::ffi;
use lua::Lua; use crate::function::Function;
use types::Callback; use crate::types::{Callback, LuaRef};
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use util::{ use crate::util::{
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata, assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
take_userdata, StackGuard, take_userdata, StackGuard,
}; };
use value::{FromLuaMulti, MultiValue, ToLuaMulti, Value}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
/// Constructed by the [`Lua::scope`] method, allows temporarily passing to Lua userdata that is /// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
/// !Send, and callbacks that are !Send and not 'static. /// callbacks that are not required to be Send or 'static.
/// ///
/// See [`Lua::scope`] for more details. /// See [`Lua::scope`] for more details.
/// ///
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
pub struct Scope<'scope> { pub struct Scope<'lua, 'scope> {
lua: &'scope Lua, lua: &'lua Lua,
destructors: RefCell<Vec<Box<Fn() -> Box<Any> + 'scope>>>, destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Box<dyn Any>)>>,
// 'scope lifetime must be invariant _scope_invariant: PhantomData<Cell<&'scope ()>>,
_scope: PhantomData<&'scope mut &'scope ()>,
} }
impl<'scope> Scope<'scope> { impl<'lua, 'scope> Scope<'lua, 'scope> {
pub(crate) fn new(lua: &'scope Lua) -> Scope { pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
Scope { Scope {
lua, lua,
destructors: RefCell::new(Vec::new()), destructors: RefCell::new(Vec::new()),
_scope: PhantomData, _scope_invariant: PhantomData,
} }
} }
/// Wraps a Rust function or closure, creating a callable Lua function handle to it. /// Wraps a Rust function or closure, creating a callable Lua function handle to it.
/// ///
/// This is a version of [`Lua::create_function`] that creates a callback which expires on scope /// This is a version of [`Lua::create_function`] that creates a callback which expires on
/// drop. See [`Lua::scope`] for more details. /// scope drop. See [`Lua::scope`] for more details.
/// ///
/// [`Lua::create_function`]: struct.Lua.html#method.create_function /// [`Lua::create_function`]: struct.Lua.html#method.create_function
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>> pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'callback>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'lua Lua, A) -> Result<R>, F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
{ {
// Safe, because 'scope must outlive 'lua (due to Self containing 'scope), however the // Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
// callback itself must be 'scope lifetime, so the function should not be able to capture // callback itself must be 'scope lifetime, so the function should not be able to capture
// anything of 'lua lifetime. 'scope can't be shortened due to being invariant, and the // anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
// 'lua lifetime here can't be enlarged due to coming from a universal quantification in // the 'callback lifetime here can't be enlarged due to coming from a universal
// Lua::scope. // quantification in Lua::scope.
// //
// I hope I got this explanation right, but in any case this is tested with compiletest_rs // I hope I got this explanation right, but in any case this is tested with compiletest_rs
// to make sure callbacks can't capture handles with lifetimes outside the scope, inside the // to make sure callbacks can't capture handles with lifetime outside the scope, inside the
// scope, and owned inside the callback itself. // scope, and owned inside the callback itself.
unsafe { unsafe {
self.create_callback(Box::new(move |lua, args| { self.create_callback(Box::new(move |lua, args| {
@ -72,17 +70,20 @@ impl<'scope> Scope<'scope> {
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it. /// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
/// ///
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires on /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
/// scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details. /// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
/// ///
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut /// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`Scope::create_function`]: #method.create_function /// [`Scope::create_function`]: #method.create_function
pub fn create_function_mut<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>> pub fn create_function_mut<'callback, A, R, F>(
&'callback self,
func: F,
) -> Result<Function<'lua>>
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'callback>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'callback>,
F: 'scope + FnMut(&'lua Lua, A) -> Result<R>, F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
{ {
let func = RefCell::new(func); let func = RefCell::new(func);
self.create_function(move |lua, args| { self.create_function(move |lua, args| {
@ -94,13 +95,13 @@ impl<'scope> Scope<'scope> {
/// Create a Lua userdata object from a custom userdata type. /// Create a Lua userdata object from a custom userdata type.
/// ///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on scope /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// drop, and does not require that the userdata type be Send (but still requires that the /// scope drop, and does not require that the userdata type be Send (but still requires that the
/// UserData be 'static). See [`Lua::scope`] for more details. /// UserData be 'static). See [`Lua::scope`] for more details.
/// ///
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata /// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_static_userdata<'lua, T>(&'lua self, data: T) -> Result<AnyUserData<'lua>> pub fn create_static_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where where
T: 'static + UserData, T: 'static + UserData,
{ {
@ -108,13 +109,12 @@ impl<'scope> Scope<'scope> {
// thread while the Scope is alive (or the returned AnyUserData handle even). // thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe { unsafe {
let u = self.lua.make_userdata(data)?; let u = self.lua.make_userdata(data)?;
let mut destructors = self.destructors.borrow_mut(); self.destructors.borrow_mut().push((u.0.clone(), |u| {
let u_destruct = u.0.clone(); let state = u.lua.state;
destructors.push(Box::new(move || { assert_stack(state, 2);
let state = u_destruct.lua.state; u.lua.push_ref(&u);
let _sg = StackGuard::new(state); // We know the destructor has not run yet because we hold a reference to the
assert_stack(state, 1); // userdata.
u_destruct.lua.push_ref(&u_destruct);
Box::new(take_userdata::<RefCell<T>>(state)) Box::new(take_userdata::<RefCell<T>>(state))
})); }));
Ok(u) Ok(u)
@ -123,9 +123,9 @@ impl<'scope> Scope<'scope> {
/// Create a Lua userdata object from a custom userdata type. /// Create a Lua userdata object from a custom userdata type.
/// ///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on scope /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// drop, and does not require that the userdata type be Send or 'static. See [`Lua::scope`] for /// scope drop, and does not require that the userdata type be Send or 'static. See
/// more details. /// [`Lua::scope`] for more details.
/// ///
/// Lifting the requirement that the UserData type be 'static comes with some important /// Lifting the requirement that the UserData type be 'static comes with some important
/// limitations, so if you only need to eliminate the Send requirement, it is probably better to /// limitations, so if you only need to eliminate the Send requirement, it is probably better to
@ -144,34 +144,34 @@ impl<'scope> Scope<'scope> {
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata /// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`UserDataMethods`]: trait.UserDataMethods.html /// [`UserDataMethods`]: trait.UserDataMethods.html
pub fn create_nonstatic_userdata<'lua, T>(&'lua self, data: T) -> Result<AnyUserData<'lua>> pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where where
T: 'scope + UserData, T: 'scope + UserData,
{ {
let data = Rc::new(RefCell::new(data)); let data = Rc::new(RefCell::new(data));
// 'callback outliving 'scope is a lie to make the types work out, required due to the // 'callback outliving 'scope is a lie to make the types work out, required due to the
// inability to work with the "correct" universally quantified callback type. This is safe // inability to work with the more correct callback type that is universally quantified over
// though, because actual method callbacks are all 'static so they can't capture 'callback // 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
// handles anyway. // lifetime, so none of the static methods UserData types can add can possibly capture
// parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>( fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &'lua Scope<'scope>, scope: &Scope<'lua, 'scope>,
data: Rc<RefCell<T>>, data: Rc<RefCell<T>>,
method: NonStaticMethod<'callback, T>, method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> { ) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in // On methods that actually receive the userdata, we fake a type check on the passed in
// userdata, where we pretend there is a unique type per call to // userdata, where we pretend there is a unique type per call to
// Scope::create_nonstatic_userdata. You can grab a method from a userdata and call it // `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
// on a mismatched userdata type, which when using normal 'static userdata will fail // it on a mismatched userdata type, which when using normal 'static userdata will fail
// with a type mismatch, but here without this check would proceed as though you had // with a type mismatch, but here without this check would proceed as though you had
// called the method on the original value (since we otherwise completely ignore the // called the method on the original value (since we otherwise completely ignore the
// first argument). // first argument).
let check_data = data.clone(); let check_data = data.clone();
let check_ud_type = move |lua: &Lua, value| { let check_ud_type = move |lua: &'callback Lua, value| {
if let Some(value) = value { if let Some(value) = value {
if let Value::UserData(u) = value { if let Value::UserData(u) = value {
unsafe { unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1); assert_stack(lua.state, 1);
lua.push_ref(&u.0); lua.push_ref(&u.0);
ffi::lua_getuservalue(lua.state, -1); ffi::lua_getuservalue(lua.state, -1);
@ -248,7 +248,7 @@ impl<'scope> Scope<'scope> {
for (k, m) in ud_methods.meta_methods { for (k, m) in ud_methods.meta_methods {
push_string(lua.state, k.name())?; push_string(lua.state, k.name())?;
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?)); lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
protect_lua_closure(lua.state, 3, 1, |state| { protect_lua_closure(lua.state, 3, 1, |state| {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
@ -263,7 +263,7 @@ impl<'scope> Scope<'scope> {
})?; })?;
for (k, m) in ud_methods.methods { for (k, m) in ud_methods.methods {
push_string(lua.state, &k)?; push_string(lua.state, &k)?;
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?)); lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
protect_lua_closure(lua.state, 3, 1, |state| { protect_lua_closure(lua.state, 3, 1, |state| {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
})?; })?;
@ -279,25 +279,26 @@ impl<'scope> Scope<'scope> {
} }
} }
// Unsafe, because the callback (since it is non-'static) can capture any value with 'callback // Unsafe, because the callback can improperly capture any value with 'callback scope, such as
// scope, such as improperly holding onto an argument. So in order for this to be safe, the // improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
// callback must NOT capture any arguments. // lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
unsafe fn create_callback<'lua, 'callback>( // a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
&'lua self, // must NOT capture any parameters.
unsafe fn create_callback<'callback>(
&self,
f: Callback<'callback, 'scope>, f: Callback<'callback, 'scope>,
) -> Result<Function<'lua>> { ) -> Result<Function<'lua>> {
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'callback, 'static>>(f); let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
let f = self.lua.create_callback(f)?; let f = self.lua.create_callback(f)?;
let mut destructors = self.destructors.borrow_mut(); let mut destructors = self.destructors.borrow_mut();
let f_destruct = f.0.clone(); destructors.push((f.0.clone(), |f| {
destructors.push(Box::new(move || { let state = f.lua.state;
let state = f_destruct.lua.state; assert_stack(state, 3);
let _sg = StackGuard::new(state); f.lua.push_ref(&f);
assert_stack(state, 2);
f_destruct.lua.push_ref(&f_destruct);
ffi::lua_getupvalue(state, -1, 1); ffi::lua_getupvalue(state, -1, 1);
// We know the destructor has not run yet because we hold a reference to the callback.
let ud = take_userdata::<Callback>(state); let ud = take_userdata::<Callback>(state);
ffi::lua_pushnil(state); ffi::lua_pushnil(state);
@ -310,98 +311,104 @@ impl<'scope> Scope<'scope> {
} }
} }
impl<'scope> Drop for Scope<'scope> { impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
fn drop(&mut self) { fn drop(&mut self) {
// We separate the action of invalidating the userdata in Lua and actually dropping the // We separate the action of invalidating the userdata in Lua and actually dropping the
// userdata type into two phases. This is so that, in the event a userdata drop panics, we // userdata type into two phases. This is so that, in the event a userdata drop panics, we
// can be sure that all of the userdata in Lua is actually invalidated. // can be sure that all of the userdata in Lua is actually invalidated.
// All destructors are non-panicking, so this is fine
let to_drop = self let to_drop = self
.destructors .destructors
.get_mut() .get_mut()
.drain(..) .drain(..)
.map(|destructor| destructor()) .map(|(r, dest)| dest(r))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
drop(to_drop); drop(to_drop);
} }
} }
enum NonStaticMethod<'lua, T> { enum NonStaticMethod<'lua, T> {
Method(Box<Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>), Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
MethodMut(Box<FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>), MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
Function(Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>), Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
FunctionMut(Box<FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>), FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
} }
struct NonStaticUserDataMethods<'lua, T: UserData> { struct NonStaticUserDataMethods<'lua, T: UserData> {
methods: HashMap<StdString, NonStaticMethod<'lua, T>>, methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
meta_methods: HashMap<MetaMethod, NonStaticMethod<'lua, T>>, meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
} }
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> { impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
fn default() -> NonStaticUserDataMethods<'lua, T> { fn default() -> NonStaticUserDataMethods<'lua, T> {
NonStaticUserDataMethods { NonStaticUserDataMethods {
methods: HashMap::new(), methods: Vec::new(),
meta_methods: HashMap::new(), meta_methods: Vec::new(),
} }
} }
} }
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> { impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
fn add_method<A, R, M>(&mut self, name: &str, method: M) fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>, M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{ {
self.methods.insert( self.methods.push((
name.to_owned(), name.as_ref().to_vec(),
NonStaticMethod::Method(Box::new(move |lua, ud, args| { NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_method_mut<A, R, M>(&mut self, name: &str, mut method: M) fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>, M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{ {
self.methods.insert( self.methods.push((
name.to_owned(), name.as_ref().to_vec(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| { NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_function<A, R, F>(&mut self, name: &str, function: F) fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>, F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{ {
self.methods.insert( self.methods.push((
name.to_owned(), name.as_ref().to_vec(),
NonStaticMethod::Function(Box::new(move |lua, args| { NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_function_mut<A, R, F>(&mut self, name: &str, mut function: F) fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>, F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.methods.insert( self.methods.push((
name.to_owned(), name.as_ref().to_vec(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| { NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M) fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
@ -410,12 +417,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>, M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{ {
self.meta_methods.insert( self.meta_methods.push((
meta, meta,
NonStaticMethod::Method(Box::new(move |lua, ud, args| { NonStaticMethod::Method(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M) fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
@ -424,12 +431,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>, M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{ {
self.meta_methods.insert( self.meta_methods.push((
meta, meta,
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| { NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F) fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
@ -438,12 +445,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>, F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods.insert( self.meta_methods.push((
meta, meta,
NonStaticMethod::Function(Box::new(move |lua, args| { NonStaticMethod::Function(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F) fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
@ -452,11 +459,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>, F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods.insert( self.meta_methods.push((
meta, meta,
NonStaticMethod::FunctionMut(Box::new(move |lua, args| { NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})), })),
); ));
} }
} }

View file

@ -1,9 +1,9 @@
use std::{slice, str}; use std::{slice, str};
use error::{Error, Result}; use crate::error::{Error, Result};
use ffi; use crate::ffi;
use types::LuaRef; use crate::types::LuaRef;
use util::{assert_stack, StackGuard}; use crate::util::{assert_stack, StackGuard};
/// Handle to an internal Lua string. /// Handle to an internal Lua string.
/// ///
@ -17,22 +17,18 @@ impl<'lua> String<'lua> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, String, Result}; /// # use rlua::{Lua, String, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let globals = lua.globals(); /// let globals = lua.globals();
/// ///
/// let version: String = globals.get("_VERSION")?; /// let version: String = globals.get("_VERSION")?;
/// assert!(version.to_str().unwrap().contains("Lua")); /// assert!(version.to_str().unwrap().contains("Lua"));
/// ///
/// let non_utf8: String = lua.eval(r#" "test\xff" "#, None)?; /// let non_utf8: String = lua.load(r#" "test\xff" "#).eval()?;
/// assert!(non_utf8.to_str().is_err()); /// assert!(non_utf8.to_str().is_err());
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
pub fn to_str(&self) -> Result<&str> { pub fn to_str(&self) -> Result<&str> {
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError { str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
@ -50,14 +46,13 @@ impl<'lua> String<'lua> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua; /// # use rlua::{Lua, String, Result};
/// # use rlua::{Lua, String}; /// # fn main() -> Result<()> {
/// # fn main() {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// /// let non_utf8: String = lua.load(r#" "test\xff" "#).eval()?;
/// let non_utf8: String = lua.eval(r#" "test\xff" "#, None).unwrap();
/// assert!(non_utf8.to_str().is_err()); // oh no :( /// assert!(non_utf8.to_str().is_err()); // oh no :(
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]); /// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
/// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn as_bytes(&self) -> &[u8] { pub fn as_bytes(&self) -> &[u8] {

View file

@ -1,11 +1,11 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os::raw::c_int; use std::os::raw::c_int;
use error::Result; use crate::error::Result;
use ffi; use crate::ffi;
use types::{Integer, LuaRef}; use crate::types::{Integer, LuaRef};
use util::{assert_stack, protect_lua, protect_lua_closure, StackGuard}; use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
use value::{FromLua, Nil, ToLua, Value}; use crate::value::{FromLua, Nil, ToLua, Value};
/// Handle to an internal Lua table. /// Handle to an internal Lua table.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -24,9 +24,8 @@ impl<'lua> Table<'lua> {
/// Export a value as a global to make it usable from Lua: /// Export a value as a global to make it usable from Lua:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result}; /// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let globals = lua.globals(); /// let globals = lua.globals();
/// ///
@ -43,9 +42,6 @@ impl<'lua> Table<'lua> {
/// "#, None)?; /// "#, None)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`raw_set`]: #method.raw_set /// [`raw_set`]: #method.raw_set
@ -58,8 +54,8 @@ impl<'lua> Table<'lua> {
assert_stack(lua.state, 6); assert_stack(lua.state, 6);
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key); lua.push_value(key)?;
lua.push_value(value); lua.push_value(value)?;
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_settable(state, -3); ffi::lua_settable(state, -3);
@ -81,9 +77,8 @@ impl<'lua> Table<'lua> {
/// Query the version of the Lua interpreter: /// Query the version of the Lua interpreter:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result}; /// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let globals = lua.globals(); /// let globals = lua.globals();
/// ///
@ -91,9 +86,6 @@ impl<'lua> Table<'lua> {
/// println!("Lua version: {}", version); /// println!("Lua version: {}", version);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`raw_get`]: #method.raw_get /// [`raw_get`]: #method.raw_get
@ -105,7 +97,7 @@ impl<'lua> Table<'lua> {
assert_stack(lua.state, 5); assert_stack(lua.state, 5);
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key); lua.push_value(key)?;
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettable(state, -2); ffi::lua_gettable(state, -2);
@ -127,7 +119,7 @@ impl<'lua> Table<'lua> {
assert_stack(lua.state, 5); assert_stack(lua.state, 5);
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key); lua.push_value(key)?;
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettable(state, -2); ffi::lua_gettable(state, -2);
@ -151,8 +143,8 @@ impl<'lua> Table<'lua> {
assert_stack(lua.state, 6); assert_stack(lua.state, 6);
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key); lua.push_value(key)?;
lua.push_value(value); lua.push_value(value)?;
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
@ -173,7 +165,7 @@ impl<'lua> Table<'lua> {
assert_stack(lua.state, 3); assert_stack(lua.state, 3);
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key); lua.push_value(key)?;
ffi::lua_rawget(lua.state, -2); ffi::lua_rawget(lua.state, -2);
lua.pop_value() lua.pop_value()
}; };
@ -261,9 +253,8 @@ impl<'lua> Table<'lua> {
/// Iterate over all globals: /// Iterate over all globals:
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result, Value}; /// # use rlua::{Lua, Result, Value};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let globals = lua.globals(); /// let globals = lua.globals();
/// ///
@ -274,9 +265,6 @@ impl<'lua> Table<'lua> {
/// } /// }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`Result`]: type.Result.html /// [`Result`]: type.Result.html
@ -307,11 +295,17 @@ impl<'lua> Table<'lua> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result, Table}; /// # use rlua::{Lua, Result, Table};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let my_table: Table = lua.eval("{ [1] = 4, [2] = 5, [4] = 7, key = 2 }", None)?; /// let my_table: Table = lua.load(r#"
/// {
/// [1] = 4,
/// [2] = 5,
/// [4] = 7,
/// key = 2
/// }
/// "#).eval()?;
/// ///
/// let expected = [4, 5]; /// let expected = [4, 5];
/// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) { /// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
@ -319,9 +313,6 @@ impl<'lua> Table<'lua> {
/// } /// }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`pairs`]: #method.pairs /// [`pairs`]: #method.pairs
@ -364,7 +355,7 @@ where
assert_stack(lua.state, 6); assert_stack(lua.state, 6);
lua.push_ref(&self.table); lua.push_ref(&self.table);
lua.push_value(next_key); lua.push_value(next_key)?;
if protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| { if protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2) != 0 ffi::lua_next(state, -2) != 0

View file

@ -1,10 +1,12 @@
use std::os::raw::c_int; use std::os::raw::c_int;
use error::{Error, Result}; use crate::error::{Error, Result};
use ffi; use crate::ffi;
use types::LuaRef; use crate::types::LuaRef;
use util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard}; use crate::util::{
use value::{FromLuaMulti, MultiValue, ToLuaMulti}; assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
/// Status of a Lua thread (or coroutine). /// Status of a Lua thread (or coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
@ -44,9 +46,8 @@ impl<'lua> Thread<'lua> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua; /// # use rlua::{Lua, Thread, Error};
/// # use rlua::{Lua, Thread, Error, Result}; /// # fn main() -> Result<()> {
/// # fn try_main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// let thread: Thread = lua.eval(r#" /// let thread: Thread = lua.eval(r#"
/// coroutine.create(function(arg) /// coroutine.create(function(arg)
@ -65,10 +66,6 @@ impl<'lua> Thread<'lua> {
/// Err(Error::CoroutineInactive) => {}, /// Err(Error::CoroutineInactive) => {},
/// unexpected => panic!("unexpected result {:?}", unexpected), /// unexpected => panic!("unexpected result {:?}", unexpected),
/// } /// }
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # } /// # }
/// ``` /// ```
pub fn resume<A, R>(&self, args: A) -> Result<R> pub fn resume<A, R>(&self, args: A) -> Result<R>
@ -80,7 +77,7 @@ impl<'lua> Thread<'lua> {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let results = unsafe { let results = unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1); assert_stack(lua.state, 3);
lua.push_ref(&self.0); lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1); let thread_state = ffi::lua_tothread(lua.state, -1);
@ -97,13 +94,16 @@ impl<'lua> Thread<'lua> {
check_stack(thread_state, nargs + 1)?; check_stack(thread_state, nargs + 1)?;
for arg in args { for arg in args {
lua.push_value(arg); lua.push_value(arg)?;
} }
ffi::lua_xmove(lua.state, thread_state, nargs); ffi::lua_xmove(lua.state, thread_state, nargs);
let ret = ffi::lua_resume(thread_state, lua.state, nargs); let ret = ffi::lua_resume(thread_state, lua.state, nargs);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
error_traceback(thread_state); protect_lua_closure(lua.state, 0, 0, |_| {
error_traceback(thread_state);
0
})?;
return Err(pop_error(thread_state, ret)); return Err(pop_error(thread_state, ret));
} }

View file

@ -2,10 +2,10 @@ use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr}; use std::{fmt, mem, ptr};
use error::Result; use crate::error::Result;
use ffi; use crate::ffi;
use lua::Lua; use crate::lua::Lua;
use value::MultiValue; use crate::value::MultiValue;
/// Type of Lua integer numbers. /// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer; pub type Integer = ffi::lua_Integer;
@ -52,7 +52,7 @@ impl fmt::Debug for RegistryKey {
impl Drop for RegistryKey { impl Drop for RegistryKey {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(list) = self.unref_list.lock().unwrap().as_mut() { if let Some(list) = rlua_expect!(self.unref_list.lock(), "unref_list poisoned").as_mut() {
list.push(self.registry_id); list.push(self.registry_id);
} }
} }

View file

@ -1,11 +1,11 @@
use std::cell::{Ref, RefCell, RefMut}; use std::cell::{Ref, RefCell, RefMut};
use error::{Error, Result}; use crate::error::{Error, Result};
use ffi; use crate::ffi;
use lua::Lua; use crate::lua::Lua;
use types::LuaRef; use crate::types::LuaRef;
use util::{assert_stack, get_userdata, StackGuard}; use crate::util::{assert_stack, get_userdata, StackGuard};
use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
/// Kinds of metamethods that can be overridden. /// Kinds of metamethods that can be overridden.
/// ///
@ -66,31 +66,31 @@ pub enum MetaMethod {
} }
impl MetaMethod { impl MetaMethod {
pub(crate) fn name(self) -> &'static str { pub(crate) fn name(self) -> &'static [u8] {
match self { match self {
MetaMethod::Add => "__add", MetaMethod::Add => b"__add",
MetaMethod::Sub => "__sub", MetaMethod::Sub => b"__sub",
MetaMethod::Mul => "__mul", MetaMethod::Mul => b"__mul",
MetaMethod::Div => "__div", MetaMethod::Div => b"__div",
MetaMethod::Mod => "__mod", MetaMethod::Mod => b"__mod",
MetaMethod::Pow => "__pow", MetaMethod::Pow => b"__pow",
MetaMethod::Unm => "__unm", MetaMethod::Unm => b"__unm",
MetaMethod::IDiv => "__idiv", MetaMethod::IDiv => b"__idiv",
MetaMethod::BAnd => "__band", MetaMethod::BAnd => b"__band",
MetaMethod::BOr => "__bor", MetaMethod::BOr => b"__bor",
MetaMethod::BXor => "__bxor", MetaMethod::BXor => b"__bxor",
MetaMethod::BNot => "__bnot", MetaMethod::BNot => b"__bnot",
MetaMethod::Shl => "__shl", MetaMethod::Shl => b"__shl",
MetaMethod::Shr => "__shr", MetaMethod::Shr => b"__shr",
MetaMethod::Concat => "__concat", MetaMethod::Concat => b"__concat",
MetaMethod::Len => "__len", MetaMethod::Len => b"__len",
MetaMethod::Eq => "__eq", MetaMethod::Eq => b"__eq",
MetaMethod::Lt => "__lt", MetaMethod::Lt => b"__lt",
MetaMethod::Le => "__le", MetaMethod::Le => b"__le",
MetaMethod::Index => "__index", MetaMethod::Index => b"__index",
MetaMethod::NewIndex => "__newindex", MetaMethod::NewIndex => b"__newindex",
MetaMethod::Call => "__call", MetaMethod::Call => b"__call",
MetaMethod::ToString => "__tostring", MetaMethod::ToString => b"__tostring",
} }
} }
} }
@ -106,8 +106,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
/// ///
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will /// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
/// be used as a fall-back if no regular method is found. /// be used as a fall-back if no regular method is found.
fn add_method<A, R, M>(&mut self, name: &str, method: M) fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>; M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
@ -117,33 +118,37 @@ pub trait UserDataMethods<'lua, T: UserData> {
/// Refer to [`add_method`] for more information about the implementation. /// Refer to [`add_method`] for more information about the implementation.
/// ///
/// [`add_method`]: #method.add_method /// [`add_method`]: #method.add_method
fn add_method_mut<A, R, M>(&mut self, name: &str, method: M) fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>; M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
/// Add a regular method as a function which accepts generic arguments, the first argument will /// Add a regular method as a function which accepts generic arguments, the first argument will
/// always be a `UserData` of type T. /// be a `UserData` of type T if the method is called with Lua method syntax:
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
/// ///
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use. /// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
/// ///
/// [`add_method`]: #method.add_method /// [`add_method`]: #method.add_method
/// [`add_method_mut`]: #method.add_method_mut /// [`add_method_mut`]: #method.add_method_mut
fn add_function<A, R, F>(&mut self, name: &str, function: F) fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>; F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
/// Add a regular method as a mutable function which accepts generic arguments, the first /// Add a regular method as a mutable function which accepts generic arguments.
/// argument will always be a `UserData` of type T.
/// ///
/// This is a version of [`add_function`] that accepts a FnMut argument. /// This is a version of [`add_function`] that accepts a FnMut argument.
/// ///
/// [`add_function`]: #method.add_function /// [`add_function`]: #method.add_function
fn add_function_mut<A, R, F>(&mut self, name: &str, function: F) fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
where where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>; F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
@ -207,9 +212,8 @@ pub trait UserDataMethods<'lua, T: UserData> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, UserData, Result}; /// # use rlua::{Lua, UserData, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// struct MyUserData(i32); /// struct MyUserData(i32);
/// ///
/// impl UserData for MyUserData {} /// impl UserData for MyUserData {}
@ -222,18 +226,14 @@ pub trait UserDataMethods<'lua, T: UserData> {
/// lua.exec::<_, ()>("assert(type(myobject) == 'userdata')", None)?; /// lua.exec::<_, ()>("assert(type(myobject) == 'userdata')", None)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// Custom methods and operators can be provided by implementing `add_methods` (refer to /// Custom methods and operators can be provided by implementing `add_methods` (refer to
/// [`UserDataMethods`] for more information): /// [`UserDataMethods`] for more information):
/// ///
/// ``` /// ```
/// # extern crate rlua;
/// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result}; /// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result};
/// # fn try_main() -> Result<()> { /// # fn main() -> Result<()> {
/// struct MyUserData(i32); /// struct MyUserData(i32);
/// ///
/// impl UserData for MyUserData { /// impl UserData for MyUserData {
@ -265,9 +265,6 @@ pub trait UserDataMethods<'lua, T: UserData> {
/// "#, None)?; /// "#, None)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ``` /// ```
/// ///
/// [`ToLua`]: trait.ToLua.html /// [`ToLua`]: trait.ToLua.html
@ -343,7 +340,7 @@ impl<'lua> AnyUserData<'lua> {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2); assert_stack(lua.state, 2);
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(v); lua.push_value(v)?;
ffi::lua_setuservalue(lua.state, -2); ffi::lua_setuservalue(lua.state, -2);
Ok(()) Ok(())
} }

View file

@ -1,12 +1,13 @@
use std::any::Any; use std::any::Any;
use std::ffi::CStr; use std::borrow::Cow;
use std::fmt::Write;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc; use std::sync::Arc;
use std::{mem, ptr}; use std::{mem, ptr, slice};
use error::{Error, Result}; use crate::error::{Error, Result};
use ffi; use crate::ffi;
// Checks that Lua has enough free stack space for future stack operations. On failure, this will // Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message. // panic with an internal error message.
@ -20,7 +21,7 @@ pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
); );
} }
// Similar to `assert_stack`, but returns `Error::StackError` on failure. // Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> { pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
if ffi::lua_checkstack(state, amount) == 0 { if ffi::lua_checkstack(state, amount) == 0 {
Err(Error::StackError) Err(Error::StackError)
@ -37,7 +38,7 @@ pub struct StackGuard {
impl StackGuard { impl StackGuard {
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the // Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at // stack size and drop any extra elements. If the stack size at the end is *smaller* than at
// the beginning, this is considered a fatal logic error and will result in an abort. // the beginning, this is considered a fatal logic error and will result in a panic.
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard { pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
StackGuard { StackGuard {
state, state,
@ -179,16 +180,10 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
if let Some(p) = (*panic).0.take() { if let Some(p) = (*panic).0.take() {
resume_unwind(p); resume_unwind(p);
} else { } else {
rlua_panic!("panic was resumed twice") rlua_panic!("error during panic handling, panic was resumed twice")
} }
} else { } else {
let err_string = gc_guard(state, || { let err_string = to_string(state, -1).into_owned();
if let Some(s) = ffi::lua_tostring(state, -1).as_ref() {
CStr::from_ptr(s).to_string_lossy().into_owned()
} else {
"<unprintable error>".to_owned()
}
});
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
match err_code { match err_code {
@ -202,17 +197,13 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
} }
} }
ffi::LUA_ERRERR => { ffi::LUA_ERRERR => {
// The Lua manual documents this error wrongly: It is not raised when a message // This error is raised when the error handler raises an error too many times
// handler errors, but rather when some specific situations regarding stack // recursively, and continuing to trigger the error handler would cause a stack
// overflow handling occurs. Since it is not very useful do differentiate // overflow. It is not very useful to differentiate between this and "ordinary"
// between that and "ordinary" runtime errors, we handle them the same way. // runtime errors, so we handle them the same way.
Error::RuntimeError(err_string) Error::RuntimeError(err_string)
} }
ffi::LUA_ERRMEM => { ffi::LUA_ERRMEM => Error::MemoryError(err_string),
// This should be impossible, as we set the lua allocator to one that aborts
// instead of failing.
rlua_abort!("impossible Lua allocation error")
}
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string), ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
_ => rlua_panic!("unrecognized lua error code"), _ => rlua_panic!("unrecognized lua error code"),
} }
@ -246,7 +237,9 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
} }
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua // Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
// userdata. // 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 { 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 // 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 // metamethods that trigger an error on access. We do this so that it will not be double
@ -336,7 +329,8 @@ pub unsafe fn init_userdata_metatable<T>(
} }
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int { pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || { callback_error(state, |_| {
check_stack(state, 1)?;
take_userdata::<T>(state); take_userdata::<T>(state);
Ok(0) Ok(0)
}) })
@ -346,24 +340,54 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a // 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 // 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. // 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.
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
where where
F: FnOnce() -> Result<R>, F: FnOnce(c_int) -> Result<R>,
{ {
match catch_unwind(AssertUnwindSafe(f)) { let nargs = ffi::lua_gettop(state);
Ok(Ok(r)) => r,
// We need one extra stack space to store preallocated memory, and at least 3 stack spaces
// overall for handling error metatables
let extra_stack = if nargs < 3 { 3 - nargs } else { 1 };
ffi::luaL_checkstack(
state,
extra_stack,
cstr!("not enough stack space for callback error handling"),
);
// We cannot shadow rust errors with Lua ones, we pre-allocate enough memory to store a wrapped
// error or panic *before* we proceed.
let ud = ffi::lua_newuserdata(
state,
mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>()),
);
ffi::lua_rotate(state, 1, 1);
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
Ok(Ok(r)) => {
ffi::lua_rotate(state, 1, -1);
ffi::lua_pop(state, 1);
r
}
Ok(Err(err)) => { Ok(Err(err)) => {
ffi::lua_settop(state, 0); ffi::lua_settop(state, 1);
ffi::luaL_checkstack(state, 2, ptr::null()); ptr::write(ud as *mut WrappedError, WrappedError(err));
push_wrapped_error(state, err); get_error_metatable(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state) ffi::lua_error(state)
} }
Err(p) => { Err(p) => {
ffi::lua_settop(state, 0); ffi::lua_settop(state, 1);
if ffi::lua_checkstack(state, 2) == 0 { ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
rlua_abort!("not enough stack space to propagate panic"); get_panic_metatable(state);
} ffi::lua_setmetatable(state, -2);
push_wrapped_panic(state, p);
ffi::lua_error(state) ffi::lua_error(state)
} }
} }
@ -371,50 +395,48 @@ where
// Takes an error at the top of the stack, and if it is a WrappedError, converts it to an // Takes an error at the top of the stack, and if it is a WrappedError, converts it to an
// Error::CallbackError with a traceback, if it is some lua type, prints the error along with a // Error::CallbackError with a traceback, if it is some lua type, prints the error along with a
// traceback, and if it is a WrappedPanic, does not modify it. This function should never panic or // traceback, and if it is a WrappedPanic, does not modify it. This function does its best to avoid
// trigger a error (longjmp). // triggering another error and shadowing previous rust errors, but it may trigger Lua errors that
// shadow rust errors under certain memory conditions. This function ensures that such behavior
// will *never* occur with a rust panic, however.
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
// I believe luaL_traceback requires this much free stack to not error. // I believe luaL_traceback requires this much free stack to not error.
const LUA_TRACEBACK_STACK: c_int = 11; const LUA_TRACEBACK_STACK: c_int = 11;
if ffi::lua_checkstack(state, 2) == 0 { if ffi::lua_checkstack(state, 2) == 0 {
// If we don't have enough stack space to even check the error type, do nothing // If we don't have enough stack space to even check the error type, do nothing so we don't
} else if let Some(error) = get_wrapped_error(state, 1).as_ref() { // risk shadowing a rust panic.
} else if let Some(error) = get_wrapped_error(state, -1).as_ref() {
// lua_newuserdata and luaL_traceback may error, but nothing that implements Drop should be
// on the rust stack at this time.
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
gc_guard(state, || { ffi::luaL_traceback(state, state, ptr::null(), 0);
ffi::luaL_traceback(state, state, ptr::null(), 0);
}); let traceback = to_string(state, -1).into_owned();
let traceback = CStr::from_ptr(ffi::lua_tostring(state, -1))
.to_string_lossy()
.into_owned();
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
traceback traceback
} else { } else {
"not enough stack space for traceback".to_owned() "<not enough stack space for traceback>".to_owned()
}; };
let error = error.clone(); let error = error.clone();
ffi::lua_pop(state, 1); ffi::lua_remove(state, -2);
push_wrapped_error( ptr::write(
state, ud,
Error::CallbackError { WrappedError(Error::CallbackError {
traceback, traceback,
cause: Arc::new(error), cause: Arc::new(error),
}, }),
); );
} else if !is_wrapped_panic(state, 1) { get_error_metatable(state);
ffi::lua_setmetatable(state, -2);
} else if !is_wrapped_panic(state, -1) {
if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
gc_guard(state, || { let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
let s = ffi::lua_tostring(state, 1); ffi::luaL_traceback(state, state, s, 0);
let s = if s.is_null() { ffi::lua_remove(state, -2);
cstr!("<unprintable lua error>")
} else {
s
};
ffi::luaL_traceback(state, state, s, 0);
ffi::lua_remove(state, -2);
});
} }
} }
1 1
@ -493,16 +515,16 @@ pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
main_state main_state
} }
// Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call // Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
// lua_checkstack. // lua_checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) { pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
gc_guard(state, || { let ud = protect_lua_closure(state, 0, 1, move |state| {
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError; ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError
ptr::write(ud, WrappedError(err)) })?;
}); ptr::write(ud, WrappedError(err));
get_error_metatable(state); get_error_metatable(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
Ok(())
} }
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it, // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
@ -528,49 +550,43 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co
} }
} }
// Runs the given function with the Lua garbage collector disabled. `rlua` assumes that all
// allocation failures are aborts, so when the garbage collector is disabled, 'm' functions that can
// cause either an allocation error or a a `__gc` metamethod error are prevented from causing errors
// at all. The given function should never panic or longjmp, because this could inadverntently
// disable the gc. This is useful when error handling must allocate, and `__gc` errors at that time
// would shadow more important errors, or be extremely difficult to handle safely.
pub unsafe fn gc_guard<R, F: FnOnce() -> R>(state: *mut ffi::lua_State, f: F) -> R {
if ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 {
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
let r = f();
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
r
} else {
f()
}
}
// Initialize the error, panic, and destructed userdata metatables. // Initialize the error, panic, and destructed userdata metatables.
pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) { pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
assert_stack(state, 8); assert_stack(state, 8);
// Create error metatable // Create error metatable
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null()); let err_buf = callback_error(state, |_| {
check_stack(state, 3)?;
callback_error(state, || {
if let Some(error) = get_wrapped_error(state, -1).as_ref() { if let Some(error) = get_wrapped_error(state, -1).as_ref() {
let error_str = error.to_string(); ffi::lua_pushlightuserdata(
gc_guard(state, || { state,
ffi::lua_pushlstring( &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
state, );
error_str.as_ptr() as *const c_char, ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
error_str.len(), let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
) ffi::lua_pop(state, 2);
});
ffi::lua_remove(state, -2);
Ok(1) (*err_buf).clear();
// Depending on how the API is used and what error types scripts are given, it may
// be possible to make this consume arbitrary amounts of memory (for example, some
// kind of recursive error structure?)
let _ = write!(&mut (*err_buf), "{}", error);
Ok(err_buf)
} else { } else {
panic!("userdata mismatch in Error metamethod"); // I'm not sure whether this is possible to trigger without bugs in rlua?
Err(Error::UserDataTypeMismatch)
} }
}) });
ffi::lua_pushlstring(
state,
(*err_buf).as_ptr() as *const c_char,
(*err_buf).len(),
);
(*err_buf).clear();
1
} }
ffi::lua_pushlightuserdata( ffi::lua_pushlightuserdata(
@ -615,7 +631,11 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null()); ffi::luaL_checkstack(state, 2, ptr::null());
push_wrapped_error(state, Error::CallbackDestructed); let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
ptr::write(ud, WrappedError(Error::CallbackDestructed));
get_error_metatable(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state) ffi::lua_error(state)
} }
@ -658,21 +678,56 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
} }
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
// Create error print buffer
ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void);
let ud = ffi::lua_newuserdata(state, mem::size_of::<String>()) as *mut String;
ptr::write(ud, String::new());
ffi::lua_newtable(state);
ffi::lua_pushstring(state, cstr!("__gc"));
ffi::lua_pushcfunction(state, userdata_destructor::<String>);
ffi::lua_rawset(state, -3);
ffi::lua_setmetatable(state, -2);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
} }
struct WrappedError(pub Error); struct WrappedError(pub Error);
struct WrappedPanic(pub Option<Box<dyn Any + Send>>); struct WrappedPanic(pub Option<Box<dyn Any + Send>>);
// Pushes a WrappedError::Panic to the top of the stack. Uses two stack spaces and does not call // Converts the given lua value to a string in a reasonable format without causing a Lua error or
// lua_checkstack. // panicking.
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<dyn Any + Send>) { unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str> {
gc_guard(state, || { match ffi::lua_type(state, index) {
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic; ffi::LUA_TNONE => "<none>".into(),
ptr::write(ud, WrappedPanic(Some(panic))) ffi::LUA_TNIL => "<nil>".into(),
}); ffi::LUA_TBOOLEAN => (ffi::lua_toboolean(state, index) != 1).to_string().into(),
ffi::LUA_TLIGHTUSERDATA => {
get_panic_metatable(state); format!("<lightuserdata {:?}>", ffi::lua_topointer(state, index)).into()
ffi::lua_setmetatable(state, -2); }
ffi::LUA_TNUMBER => {
let mut isint = 0;
let i = ffi::lua_tointegerx(state, -1, &mut isint);
if isint == 0 {
ffi::lua_tonumber(state, index).to_string().into()
} else {
i.to_string().into()
}
}
ffi::LUA_TSTRING => {
let mut size = 0;
let data = ffi::lua_tolstring(state, index, &mut size);
String::from_utf8_lossy(slice::from_raw_parts(data as *const u8, size))
}
ffi::LUA_TTABLE => format!("<table {:?}>", ffi::lua_topointer(state, index)).into(),
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)).into(),
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)).into(),
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)).into(),
_ => "<unknown>".into(),
}
} }
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call // Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
@ -720,3 +775,4 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0; static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
static PANIC_METATABLE_REGISTRY_KEY: u8 = 0; static PANIC_METATABLE_REGISTRY_KEY: u8 = 0;
static DESTRUCTED_USERDATA_METATABLE: u8 = 0; static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static ERROR_PRINT_BUFFER_KEY: u8 = 0;

View file

@ -1,14 +1,14 @@
use std::iter::{self, FromIterator}; use std::iter::{self, FromIterator};
use std::{slice, str, vec}; use std::{slice, str, vec};
use error::{Error, Result}; use crate::error::{Error, Result};
use function::Function; use crate::function::Function;
use lua::Lua; use crate::lua::Lua;
use string::String; use crate::string::String;
use table::Table; use crate::table::Table;
use thread::Thread; use crate::thread::Thread;
use types::{Integer, LightUserData, Number}; use crate::types::{Integer, LightUserData, Number};
use userdata::AnyUserData; use crate::userdata::AnyUserData;
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData` /// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle /// variants contain handle types into the internal Lua state. It is a logic error to mix handle
@ -86,6 +86,12 @@ impl<'lua> MultiValue<'lua> {
} }
} }
impl<'lua> Default for MultiValue<'lua> {
fn default() -> MultiValue<'lua> {
MultiValue::new()
}
}
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> { impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self { fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue::from_vec(Vec::from_iter(iter)) MultiValue::from_vec(Vec::from_iter(iter))
@ -138,6 +144,10 @@ impl<'lua> MultiValue<'lua> {
self.0.len() self.0.len()
} }
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> { pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev() self.0.iter().rev()
} }