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

View file

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

View file

@ -1,16 +1,16 @@
use std::error::Error as StdError;
use std::fmt;
use std::result::Result as StdResult;
use std::string::String as StdString;
use std::sync::Arc;
use failure;
/// Error type returned by `rlua` methods.
#[derive(Debug, Clone)]
pub enum Error {
/// Syntax error while parsing Lua source code.
SyntaxError {
/// 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.
///
/// 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.
/// Among other things, this includes invoking operators on wrong types (such as calling or
/// 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`.
///
/// 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.
///
/// 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.
to: &'static str,
/// 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.
FromLuaConversionError {
@ -62,7 +67,7 @@ pub enum Error {
/// Name of the Rust type that could not be created.
to: &'static str,
/// A string containing more detailed error information.
message: Option<String>,
message: Option<StdString>,
},
/// [`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.
CallbackError {
/// Lua call stack backtrace.
traceback: String,
traceback: StdString,
/// Original error returned by the Rust code.
cause: Arc<Error>,
},
@ -118,7 +123,7 @@ pub enum Error {
/// 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`,
/// 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.
@ -129,6 +134,9 @@ impl fmt::Display for Error {
match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
Error::MemoryError(ref msg) => {
write!(fmt, "memory error: {}", msg)
}
Error::GarbageCollectorError(ref msg) => {
write!(fmt, "garbage collector error: {}", msg)
}
@ -174,34 +182,27 @@ impl fmt::Display for Error {
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref traceback, .. } => {
write!(fmt, "callback error: {}", traceback)
Error::CallbackError { ref traceback, ref cause } => {
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 {
fn cause(&self) -> Option<&dyn failure::Fail> {
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
Error::ExternalError(ref err) => err.as_fail().cause(),
_ => None,
}
}
fn backtrace(&self) -> Option<&failure::Backtrace> {
match *self {
Error::ExternalError(ref err) => Some(err.backtrace()),
Error::ExternalError(ref err) => Some(err.as_ref()),
_ => None,
}
}
}
impl Error {
pub fn external<T: Into<failure::Error>>(err: T) -> Error {
Error::ExternalError(Arc::new(err.into()))
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
Error::ExternalError(err.into().into())
}
}
@ -211,7 +212,7 @@ pub trait ExternalError {
impl<E> ExternalError for E
where
E: Into<failure::Error>,
E: Into<Box<dyn StdError + Send + Sync>>,
{
fn to_lua_err(self) -> Error {
Error::external(self)

View file

@ -23,11 +23,11 @@
//! Contains definitions from `lauxlib.h`.
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 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::LUA_FILEHANDLE;

View file

@ -23,9 +23,10 @@
//! Contains definitions from `lua.h`.
use libc::{c_void, c_int, c_char, c_uchar, size_t};
use ffi::luaconf;
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, LUA_RELEASE, LUA_COPYRIGHT, LUA_AUTHORS};
pub use super::glue::{LUA_REGISTRYINDEX};

View file

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

View file

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

View file

@ -40,15 +40,12 @@
// warnings at all.
#![doc(test(attr(deny(warnings))))]
extern crate failure;
extern crate libc;
extern crate num_traits;
mod error;
mod ffi;
#[macro_use]
mod macros;
mod conversion;
mod error;
mod ffi;
mod function;
mod lua;
mod multi;
@ -61,18 +58,18 @@ mod userdata;
mod util;
mod value;
pub use error::{Error, ExternalError, ExternalResult, Result};
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 crate::ffi::lua_State;
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;

View file

@ -4,35 +4,33 @@ use std::collections::HashMap;
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::{c_char, c_int, c_void};
use std::string::String as StdString;
use std::sync::{Arc, Mutex};
use std::{mem, ptr, str};
use libc;
use error::{Error, Result};
use ffi;
use function::Function;
use scope::Scope;
use string::String;
use table::Table;
use thread::Thread;
use types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey};
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use util::{
assert_stack, callback_error, check_stack, gc_guard, get_userdata, get_wrapped_error,
init_error_metatables, init_userdata_metatable, main_state, pop_error, protect_lua,
protect_lua_closure, push_string, push_userdata, push_wrapped_error, safe_pcall, safe_xpcall,
userdata_destructor, StackGuard,
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::scope::Scope;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use crate::util::{
assert_stack, callback_error, check_stack, get_userdata, get_wrapped_error,
init_error_registry, init_userdata_metatable, main_state, pop_error,
protect_lua, protect_lua_closure, safe_pcall, safe_xpcall,
push_string, push_userdata, push_wrapped_error, StackGuard,
userdata_destructor,
};
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.
pub struct Lua {
pub(crate) state: *mut ffi::lua_State,
main_state: *mut ffi::lua_State,
// Lua has lots of interior mutability, should not be RefUnwindSafe
_phantom: PhantomData<UnsafeCell<()>>,
_no_ref_unwind_safe: PhantomData<UnsafeCell<()>>,
}
unsafe impl Send for Lua {}
@ -42,33 +40,55 @@ impl Lua {
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
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(
state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
ffi::lua_pushlightuserdata(
state,
&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"
let extra = Box::into_raw(Box::new(ExtraData {
@ -80,97 +100,148 @@ impl Lua {
ref_stack_max: 0,
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");
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 {
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
/// results in better error traces.
/// It may be necessary to call this function twice to collect all currently unreachable
/// 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.
pub fn load<S>(&self, source: &S, name: Option<&str>) -> Result<Function>
/// Returns true if this has finished a collection cycle.
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
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 {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1);
let source = source.as_ref();
match if let Some(name) = name {
let name =
CString::new(name.to_owned()).map_err(|e| Error::ToLuaConversionError {
from: "&str",
to: "string",
message: Some(e.to_string()),
})?;
ffi::luaL_loadbuffer(
ffi::luaL_loadbufferx(
self.state,
source.as_ptr() as *const c_char,
source.len(),
name.as_ptr(),
name.as_ptr() as *const c_char,
cstr!("t"),
)
} else {
ffi::luaL_loadbuffer(
ffi::luaL_loadbufferx(
self.state,
source.as_ptr() as *const c_char,
source.len(),
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)),
}
}
}
/// 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
/// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]`
/// here.
@ -220,8 +291,8 @@ impl Lua {
protect_lua(self.state, 0, new_table)?;
for (k, v) in cont {
self.push_value(k.to_lua(self)?);
self.push_value(v.to_lua(self)?);
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, -3);
1
@ -257,9 +328,8 @@ impl Lua {
/// Create a function which prints its argument:
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> {
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
///
/// let greet = lua.create_function(|_, name: String| {
@ -269,17 +339,13 @@ impl Lua {
/// # let _ = greet; // used
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// Use tuples to accept multiple arguments:
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> {
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
///
/// let print_person = lua.create_function(|_, (name, age): (String, u8)| {
@ -289,9 +355,6 @@ impl Lua {
/// # let _ = print_person; // used
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// [`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
/// 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
/// outlive the scope lifetime.
///
/// Handles that `Lua::scope` produces have a `'lua` lifetime of the scope parameter, to prevent
/// the handles from escaping the callback. However, this is not the only way for values to
/// escape the callback, as they can be smuggled through Lua itself. This is safe to do, but
/// not very useful, because after the scope is dropped, all references to scoped values,
/// whether in Lua or in rust, are invalidated. `Function` types will error when called, and
/// `AnyUserData` types will be typeless.
/// Inside the scope callback, all handles created through Scope will share the same unique 'lua
/// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
/// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
/// However, this also enables handles to scoped values to be trivially leaked from the given
/// callback. This is not dangerous, though! After the callback returns, all scoped values are
/// 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
where
F: FnOnce(&Scope<'scope>) -> R,
F: FnOnce(&Scope<'lua, 'scope>) -> R,
{
let scope = Scope::new(self);
let r = f(&scope);
drop(scope);
r
f(&Scope::new(self))
}
/// 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
/// number.
pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Option<String<'lua>> {
match v {
pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Result<Option<String<'lua>>> {
Ok(match v {
Value::String(s) => Some(s),
v => unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
self.push_value(v);
let s = gc_guard(self.state, || ffi::lua_tostring(self.state, -1));
if s.is_null() {
None
} else {
self.push_value(v)?;
if protect_lua_closure(self.state, 1, 1, |state| {
!ffi::lua_tostring(state, -1).is_null()
})? {
Some(String(self.pop_ref()))
} else {
None
}
},
}
})
}
/// 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
/// representation as an integer, or a string that can be converted to an integer. Refer to the
/// Lua manual for details.
pub fn coerce_integer(&self, v: Value) -> Option<Integer> {
match v {
pub fn coerce_integer(&self, v: Value) -> Result<Option<Integer>> {
Ok(match v {
Value::Integer(i) => Some(i),
v => unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
self.push_value(v);
self.push_value(v)?;
let mut isint = 0;
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
if isint == 0 {
@ -437,7 +510,7 @@ impl Lua {
Some(i)
}
},
}
})
}
/// 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 the Lua manual for details.
pub fn coerce_number(&self, v: Value) -> Option<Number> {
match v {
pub fn coerce_number(&self, v: Value) -> Result<Option<Number>> {
Ok(match v {
Value::Number(n) => Some(n),
v => unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
self.push_value(v);
self.push_value(v)?;
let mut isnum = 0;
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
if isnum == 0 {
@ -461,7 +534,7 @@ impl Lua {
Some(n)
}
},
}
})
}
/// 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
/// state.
pub fn set_named_registry_value<'lua, T: ToLua<'lua>>(
pub fn set_named_registry_value<'lua, S, T>(
&'lua self,
name: &str,
name: &S,
t: T,
) -> Result<()> {
) -> Result<()>
where
S: ?Sized + AsRef<[u8]>,
T: ToLua<'lua>,
{
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 5);
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 {
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
@ -518,7 +595,11 @@ impl Lua {
/// get a value previously set by [`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 _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
@ -540,7 +621,10 @@ impl Lua {
/// Equivalent to calling [`set_named_registry_value`] with a value of Nil.
///
/// [`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)
}
@ -548,16 +632,21 @@ impl Lua {
///
/// This value will be available to rust from all `Lua` instances which share the same main
/// 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> {
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
self.push_value(t);
let registry_id = gc_guard(self.state, || {
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
});
self.push_value(t)?;
let registry_id = protect_lua_closure(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
Ok(RegistryKey {
registry_id,
@ -634,13 +723,13 @@ impl Lua {
pub fn expire_registry_values(&self) {
unsafe {
let unref_list = mem::replace(
&mut *(*extra_data(self.state))
.registry_unref_list
.lock()
.unwrap(),
&mut *rlua_expect!(
(*extra_data(self.state)).registry_unref_list.lock(),
"unref list poisoned"
),
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);
}
}
@ -648,7 +737,7 @@ impl Lua {
// Uses 2 stack spaces, does not call checkstack
// TODO: return to original
pub unsafe fn push_value(&self, value: Value) {
pub unsafe fn push_value(&self, value: Value) -> Result<()> {
match value {
Value::Nil => {
ffi::lua_pushnil(self.state);
@ -691,9 +780,11 @@ impl Lua {
}
Value::Error(e) => {
push_wrapped_error(self.state, e);
push_wrapped_error(self.state, e)?;
}
}
Ok(())
}
// Uses 2 stack spaces, does not call checkstack
@ -717,15 +808,17 @@ impl Lua {
ud
}
ffi::LUA_TNUMBER => if ffi::lua_isinteger(self.state, -1) != 0 {
let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
ffi::lua_pop(self.state, 1);
i
} else {
let n = Value::Number(ffi::lua_tonumber(self.state, -1));
ffi::lua_pop(self.state, 1);
n
},
ffi::LUA_TNUMBER => {
if ffi::lua_isinteger(self.state, -1) != 0 {
let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
ffi::lua_pop(self.state, 1);
i
} else {
let n = Value::Number(ffi::lua_tonumber(self.state, -1));
ffi::lua_pop(self.state, 1);
n
}
}
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,
// 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
// 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
@ -816,7 +909,7 @@ impl Lua {
})?;
for (k, m) in methods.meta_methods {
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| {
ffi::lua_rawset(state, -3);
@ -831,7 +924,7 @@ impl Lua {
})?;
for (k, m) in methods.methods {
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| {
ffi::lua_rawset(state, -3);
})?;
@ -841,9 +934,9 @@ impl Lua {
ffi::lua_pop(self.state, 1);
}
let id = gc_guard(self.state, || {
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
});
let id = protect_lua_closure(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
(*extra_data(self.state))
.registered_userdata
.insert(TypeId::of::<T>(), id);
@ -863,12 +956,11 @@ impl Lua {
func: Callback<'callback, 'static>,
) -> Result<Function<'lua>> {
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || {
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
callback_error(state, |nargs| {
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
return Err(Error::CallbackDestructed);
}
let nargs = ffi::lua_gettop(state);
if nargs < ffi::LUA_MINSTACK {
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
}
@ -876,7 +968,7 @@ impl Lua {
let lua = Lua {
state: state,
main_state: main_state(state),
_phantom: PhantomData,
_no_ref_unwind_safe: PhantomData,
};
let mut args = MultiValue::new();
@ -892,7 +984,7 @@ impl Lua {
check_stack(state, nresults)?;
for r in results {
lua.push_value(r);
lua.push_value(r)?;
}
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)
}
/// 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 {
if let Some(free) = (*extra).ref_free.pop() {
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
// the ref thread.
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;
}
@ -978,60 +1153,64 @@ unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
methods: HashMap<StdString, Callback<'lua, 'static>>,
meta_methods: HashMap<MetaMethod, Callback<'lua, 'static>>,
methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
_type: PhantomData<T>,
}
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
fn default() -> StaticUserDataMethods<'lua, T> {
StaticUserDataMethods {
methods: HashMap::new(),
meta_methods: HashMap::new(),
methods: Vec::new(),
meta_methods: Vec::new(),
_type: PhantomData,
}
}
}
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{
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)
@ -1040,7 +1219,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
R: ToLuaMulti<'lua>,
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)
@ -1049,7 +1228,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
R: ToLuaMulti<'lua>,
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)
@ -1058,7 +1237,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
R: ToLuaMulti<'lua>,
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)
@ -1068,7 +1247,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{
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 {
($s:expr) => {
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 {
($msg:expr) => {
panic!(concat!("rlua internal error: ", $msg));
panic!(bug_msg!($msg));
};
($msg:expr, $($arg:tt)+) => {
panic!(concat!("rlua internal error: ", $msg), $($arg)+);
($msg:expr,) => {
rlua_panic!($msg);
};
($msg:expr, $($arg:expr),+) => {
panic!(bug_msg!($msg), $($arg),+);
};
($msg:expr, $($arg:expr),+,) => {
rlua_panic!($msg, $($arg),+);
};
}
macro_rules! rlua_assert {
($cond:expr, $msg:expr) => {
assert!($cond, concat!("rlua internal error: ", $msg));
assert!($cond, bug_msg!($msg));
};
($cond:expr, $msg:expr, $($arg:tt)+) => {
assert!($cond, concat!("rlua internal error: ", $msg), $($arg)+);
($cond:expr, $msg:expr,) => {
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 {
($cond:expr, $msg:expr) => {
debug_assert!($cond, concat!("rlua internal error: ", $msg));
debug_assert!($cond, bug_msg!($msg));
};
($cond:expr, $msg:expr, $($arg:tt)+) => {
debug_assert!($cond, concat!("rlua internal error: ", $msg), $($arg)+);
($cond:expr, $msg:expr,) => {
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 {
($msg:expr) => {
{
abort!(concat!("rlua internal error: ", $msg));
}
macro_rules! rlua_expect {
($res:expr, $msg:expr) => {
$res.expect(bug_msg!($msg))
};
($msg:expr, $($arg:tt)+) => {
{
abort!(concat!("rlua internal error, aborting!: ", $msg), $($arg)+);
}
($res:expr, $msg:expr,) => {
rlua_expect!($res, $msg)
};
}

View file

@ -2,9 +2,9 @@ use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult;
use error::Result;
use lua::Lua;
use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
use crate::error::Result;
use crate::lua::Lua;
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// 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.
@ -62,21 +62,16 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
/// # Examples
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Variadic, Result};
/// # fn try_main() -> Result<()> {
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
///
/// let add = lua.create_function(|_, vals: Variadic<f64>| -> Result<f64> {
/// Ok(vals.iter().sum())
/// }).unwrap();
/// 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(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// [`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> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Variadic(Vec::from_iter(iter))

View file

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

View file

@ -1,67 +1,65 @@
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::rc::Rc;
use std::string::String as StdString;
use std::cell::Cell;
use error::{Error, Result};
use ffi;
use function::Function;
use lua::Lua;
use types::Callback;
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use util::{
use crate::error::{Error, Result};
use crate::lua::Lua;
use crate::ffi;
use crate::function::Function;
use crate::types::{Callback, LuaRef};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
use crate::util::{
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
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
/// !Send, and callbacks that are !Send and not 'static.
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
/// callbacks that are not required to be Send or 'static.
///
/// See [`Lua::scope`] for more details.
///
/// [`Lua::scope`]: struct.Lua.html#method.scope
pub struct Scope<'scope> {
lua: &'scope Lua,
destructors: RefCell<Vec<Box<Fn() -> Box<Any> + 'scope>>>,
// 'scope lifetime must be invariant
_scope: PhantomData<&'scope mut &'scope ()>,
pub struct Scope<'lua, 'scope> {
lua: &'lua Lua,
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Box<dyn Any>)>>,
_scope_invariant: PhantomData<Cell<&'scope ()>>,
}
impl<'scope> Scope<'scope> {
pub(crate) fn new(lua: &'scope Lua) -> Scope {
impl<'lua, 'scope> Scope<'lua, 'scope> {
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
Scope {
lua,
destructors: RefCell::new(Vec::new()),
_scope: PhantomData,
_scope_invariant: PhantomData,
}
}
/// 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
/// drop. See [`Lua::scope`] for more details.
/// This is a version of [`Lua::create_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] for more details.
///
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
/// [`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
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'scope + Fn(&'lua Lua, A) -> Result<R>,
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
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
// anything of 'lua lifetime. 'scope can't be shortened due to being invariant, and the
// 'lua lifetime here can't be enlarged due to coming from a universal quantification in
// Lua::scope.
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
// the 'callback lifetime here can't be enlarged due to coming from a universal
// quantification in Lua::scope.
//
// 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.
unsafe {
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.
///
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
/// 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::scope`]: struct.Lua.html#method.scope
/// [`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
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'scope + FnMut(&'lua Lua, A) -> Result<R>,
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
@ -94,13 +95,13 @@ impl<'scope> Scope<'scope> {
/// 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
/// drop, and does not require that the userdata type be Send (but still requires that the
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// 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.
///
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`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
T: 'static + UserData,
{
@ -108,13 +109,12 @@ impl<'scope> Scope<'scope> {
// thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe {
let u = self.lua.make_userdata(data)?;
let mut destructors = self.destructors.borrow_mut();
let u_destruct = u.0.clone();
destructors.push(Box::new(move || {
let state = u_destruct.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 1);
u_destruct.lua.push_ref(&u_destruct);
self.destructors.borrow_mut().push((u.0.clone(), |u| {
let state = u.lua.state;
assert_stack(state, 2);
u.lua.push_ref(&u);
// We know the destructor has not run yet because we hold a reference to the
// userdata.
Box::new(take_userdata::<RefCell<T>>(state))
}));
Ok(u)
@ -123,9 +123,9 @@ impl<'scope> Scope<'scope> {
/// 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
/// drop, and does not require that the userdata type be Send or 'static. See [`Lua::scope`] for
/// more details.
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send or 'static. See
/// [`Lua::scope`] for more details.
///
/// 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
@ -144,34 +144,34 @@ impl<'scope> Scope<'scope> {
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
/// [`Lua::scope`]: struct.Lua.html#method.scope
/// [`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
T: 'scope + UserData,
{
let data = Rc::new(RefCell::new(data));
// '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
// though, because actual method callbacks are all 'static so they can't capture 'callback
// handles anyway.
// inability to work with the more correct callback type that is universally quantified over
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
// lifetime, so none of the static methods UserData types can add can possibly capture
// parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &'lua Scope<'scope>,
scope: &Scope<'lua, 'scope>,
data: Rc<RefCell<T>>,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// 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
// Scope::create_nonstatic_userdata. You can grab a method from a userdata and call it
// on a mismatched userdata type, which when using normal 'static userdata will fail
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
// 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
// called the method on the original value (since we otherwise completely ignore the
// first argument).
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 Value::UserData(u) = value {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&u.0);
ffi::lua_getuservalue(lua.state, -1);
@ -248,7 +248,7 @@ impl<'scope> Scope<'scope> {
for (k, m) in ud_methods.meta_methods {
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| {
ffi::lua_rawset(state, -3);
@ -263,7 +263,7 @@ impl<'scope> Scope<'scope> {
})?;
for (k, m) in ud_methods.methods {
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| {
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
// scope, such as improperly holding onto an argument. So in order for this to be safe, the
// callback must NOT capture any arguments.
unsafe fn create_callback<'lua, 'callback>(
&'lua self,
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
// must NOT capture any parameters.
unsafe fn create_callback<'callback>(
&self,
f: Callback<'callback, 'scope>,
) -> 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 mut destructors = self.destructors.borrow_mut();
let f_destruct = f.0.clone();
destructors.push(Box::new(move || {
let state = f_destruct.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
f_destruct.lua.push_ref(&f_destruct);
destructors.push((f.0.clone(), |f| {
let state = f.lua.state;
assert_stack(state, 3);
f.lua.push_ref(&f);
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);
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) {
// 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
// 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
.destructors
.get_mut()
.drain(..)
.map(|destructor| destructor())
.map(|(r, dest)| dest(r))
.collect::<Vec<_>>();
drop(to_drop);
}
}
enum NonStaticMethod<'lua, T> {
Method(Box<Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
MethodMut(Box<FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
Function(Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
FunctionMut(Box<FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
}
struct NonStaticUserDataMethods<'lua, T: UserData> {
methods: HashMap<StdString, NonStaticMethod<'lua, T>>,
meta_methods: HashMap<MetaMethod, NonStaticMethod<'lua, T>>,
methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
}
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
fn default() -> NonStaticUserDataMethods<'lua, T> {
NonStaticUserDataMethods {
methods: HashMap::new(),
meta_methods: HashMap::new(),
methods: Vec::new(),
meta_methods: Vec::new(),
}
}
}
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.methods.insert(
name.to_owned(),
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.methods.insert(
name.to_owned(),
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{
self.methods.insert(
name.to_owned(),
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::Function(Box::new(move |lua, args| {
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
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{
self.methods.insert(
name.to_owned(),
self.methods.push((
name.as_ref().to_vec(),
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
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)
@ -410,12 +417,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
{
self.meta_methods.insert(
self.meta_methods.push((
meta,
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
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)
@ -424,12 +431,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
{
self.meta_methods.insert(
self.meta_methods.push((
meta,
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
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)
@ -438,12 +445,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
{
self.meta_methods.insert(
self.meta_methods.push((
meta,
NonStaticMethod::Function(Box::new(move |lua, args| {
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)
@ -452,11 +459,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
{
self.meta_methods.insert(
self.meta_methods.push((
meta,
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
})),
);
));
}
}

View file

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

View file

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

View file

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

View file

@ -2,10 +2,10 @@ use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex};
use std::{fmt, mem, ptr};
use error::Result;
use ffi;
use lua::Lua;
use value::MultiValue;
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::value::MultiValue;
/// Type of Lua integer numbers.
pub type Integer = ffi::lua_Integer;
@ -52,7 +52,7 @@ impl fmt::Debug for RegistryKey {
impl Drop for RegistryKey {
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);
}
}

View file

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

View file

@ -1,12 +1,13 @@
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::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc;
use std::{mem, ptr};
use std::{mem, ptr, slice};
use error::{Error, Result};
use ffi;
use crate::error::{Error, Result};
use crate::ffi;
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
// 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<()> {
if ffi::lua_checkstack(state, amount) == 0 {
Err(Error::StackError)
@ -37,7 +38,7 @@ pub struct StackGuard {
impl StackGuard {
// 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
// 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 {
StackGuard {
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() {
resume_unwind(p);
} else {
rlua_panic!("panic was resumed twice")
rlua_panic!("error during panic handling, panic was resumed twice")
}
} else {
let err_string = gc_guard(state, || {
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()
}
});
let err_string = to_string(state, -1).into_owned();
ffi::lua_pop(state, 1);
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 => {
// The Lua manual documents this error wrongly: It is not raised when a message
// handler errors, but rather when some specific situations regarding stack
// overflow handling occurs. Since it is not very useful do differentiate
// between that and "ordinary" runtime errors, we handle them the same way.
// This error is raised when the error handler raises an error too many times
// recursively, and continuing to trigger the error handler would cause a stack
// overflow. It is not very useful to differentiate between this and "ordinary"
// runtime errors, so we handle them the same way.
Error::RuntimeError(err_string)
}
ffi::LUA_ERRMEM => {
// 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_ERRMEM => Error::MemoryError(err_string),
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
_ => 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
// 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 {
// 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
@ -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 {
callback_error(state, || {
callback_error(state, |_| {
check_stack(state, 1)?;
take_userdata::<T>(state);
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
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
// the rust side, it will resume the panic.
//
// This function assumes the structure of the stack at the beginning of a callback, that the only
// elements on the stack are the arguments to the callback.
//
// This function uses some of the bottom of the stack for error handling, the given callback will be
// given the number of arguments available as an argument, and should return the number of returns
// as normal, but cannot assume that the arguments available start at 0.
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
where
F: FnOnce() -> Result<R>,
F: FnOnce(c_int) -> Result<R>,
{
match catch_unwind(AssertUnwindSafe(f)) {
Ok(Ok(r)) => r,
let nargs = ffi::lua_gettop(state);
// 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)) => {
ffi::lua_settop(state, 0);
ffi::luaL_checkstack(state, 2, ptr::null());
push_wrapped_error(state, err);
ffi::lua_settop(state, 1);
ptr::write(ud as *mut WrappedError, WrappedError(err));
get_error_metatable(state);
ffi::lua_setmetatable(state, -2);
ffi::lua_error(state)
}
Err(p) => {
ffi::lua_settop(state, 0);
if ffi::lua_checkstack(state, 2) == 0 {
rlua_abort!("not enough stack space to propagate panic");
}
push_wrapped_panic(state, p);
ffi::lua_settop(state, 1);
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
get_panic_metatable(state);
ffi::lua_setmetatable(state, -2);
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
// 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
// trigger a error (longjmp).
// traceback, and if it is a WrappedPanic, does not modify it. This function does its best to avoid
// 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 {
// I believe luaL_traceback requires this much free stack to not error.
const LUA_TRACEBACK_STACK: c_int = 11;
if ffi::lua_checkstack(state, 2) == 0 {
// If we don't have enough stack space to even check the error type, do nothing
} else if let Some(error) = get_wrapped_error(state, 1).as_ref() {
// If we don't have enough stack space to even check the error type, do nothing so we don't
// 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 {
gc_guard(state, || {
ffi::luaL_traceback(state, state, ptr::null(), 0);
});
let traceback = CStr::from_ptr(ffi::lua_tostring(state, -1))
.to_string_lossy()
.into_owned();
ffi::luaL_traceback(state, state, ptr::null(), 0);
let traceback = to_string(state, -1).into_owned();
ffi::lua_pop(state, 1);
traceback
} else {
"not enough stack space for traceback".to_owned()
"<not enough stack space for traceback>".to_owned()
};
let error = error.clone();
ffi::lua_pop(state, 1);
ffi::lua_remove(state, -2);
push_wrapped_error(
state,
Error::CallbackError {
ptr::write(
ud,
WrappedError(Error::CallbackError {
traceback,
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 {
gc_guard(state, || {
let s = ffi::lua_tostring(state, 1);
let s = if s.is_null() {
cstr!("<unprintable lua error>")
} else {
s
};
ffi::luaL_traceback(state, state, s, 0);
ffi::lua_remove(state, -2);
});
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
ffi::luaL_traceback(state, state, s, 0);
ffi::lua_remove(state, -2);
}
}
1
@ -493,16 +515,16 @@ pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_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.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
gc_guard(state, || {
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
ptr::write(ud, WrappedError(err))
});
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
let ud = protect_lua_closure(state, 0, 1, move |state| {
ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError
})?;
ptr::write(ud, WrappedError(err));
get_error_metatable(state);
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,
@ -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.
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);
// Create error metatable
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null());
callback_error(state, || {
let err_buf = callback_error(state, |_| {
check_stack(state, 3)?;
if let Some(error) = get_wrapped_error(state, -1).as_ref() {
let error_str = error.to_string();
gc_guard(state, || {
ffi::lua_pushlstring(
state,
error_str.as_ptr() as *const c_char,
error_str.len(),
)
});
ffi::lua_remove(state, -2);
ffi::lua_pushlightuserdata(
state,
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
ffi::lua_pop(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 {
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(
@ -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 {
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)
}
@ -658,21 +678,56 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
}
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 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
// lua_checkstack.
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<dyn Any + Send>) {
gc_guard(state, || {
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic;
ptr::write(ud, WrappedPanic(Some(panic)))
});
get_panic_metatable(state);
ffi::lua_setmetatable(state, -2);
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
// panicking.
unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str> {
match ffi::lua_type(state, index) {
ffi::LUA_TNONE => "<none>".into(),
ffi::LUA_TNIL => "<nil>".into(),
ffi::LUA_TBOOLEAN => (ffi::lua_toboolean(state, index) != 1).to_string().into(),
ffi::LUA_TLIGHTUSERDATA => {
format!("<lightuserdata {:?}>", ffi::lua_topointer(state, index)).into()
}
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
@ -720,3 +775,4 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
static PANIC_METATABLE_REGISTRY_KEY: 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::{slice, str, vec};
use error::{Error, Result};
use function::Function;
use lua::Lua;
use string::String;
use table::Table;
use thread::Thread;
use types::{Integer, LightUserData, Number};
use userdata::AnyUserData;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number};
use crate::userdata::AnyUserData;
/// 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
@ -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> {
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue::from_vec(Vec::from_iter(iter))
@ -138,6 +144,10 @@ impl<'lua> MultiValue<'lua> {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev()
}