mlua/src/lua.rs

1650 lines
56 KiB
Rust
Raw Normal View History

2017-05-21 18:50:59 -05:00
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::iter::FromIterator;
use std::cell::{RefCell, Ref, RefMut};
use std::ptr;
use std::mem;
use std::ffi::{CStr, CString};
use std::any::TypeId;
use std::marker::PhantomData;
use std::collections::{HashMap, VecDeque};
use std::collections::hash_map::Entry as HashMapEntry;
use std::os::raw::{c_char, c_int, c_void};
use ffi;
use error::*;
use util::*;
2017-06-17 20:23:17 -05:00
/// A dynamically typed Lua value.
2017-05-21 18:50:59 -05:00
#[derive(Debug, Clone)]
pub enum LuaValue<'lua> {
2017-06-17 20:23:17 -05:00
/// The Lua value `nil`.
2017-05-21 18:50:59 -05:00
Nil,
2017-06-17 20:23:17 -05:00
/// The Lua value `true` or `false`.
2017-05-21 18:50:59 -05:00
Boolean(bool),
2017-06-17 20:23:17 -05:00
/// A "light userdata" object, equivalent to a raw pointer.
LightUserData(LightUserData),
2017-06-17 20:23:17 -05:00
/// An integer number.
///
/// Any Lua number convertible to a `LuaInteger` will be represented as this variant.
2017-05-21 18:50:59 -05:00
Integer(LuaInteger),
2017-06-17 20:23:17 -05:00
/// A floating point number.
2017-05-21 18:50:59 -05:00
Number(LuaNumber),
2017-06-17 20:23:17 -05:00
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
2017-05-21 18:50:59 -05:00
String(LuaString<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a Lua table.
2017-05-21 18:50:59 -05:00
Table(LuaTable<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a Lua function (or closure).
2017-05-21 18:50:59 -05:00
Function(LuaFunction<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a "full" userdata object.
2017-05-21 18:50:59 -05:00
UserData(LuaUserData<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a Lua thread (or coroutine).
Thread(LuaThread<'lua>),
2017-05-21 18:50:59 -05:00
}
pub use self::LuaValue::Nil as LuaNil;
2017-06-17 20:23:17 -05:00
/// Trait for types convertible to `LuaValue`.
2017-05-21 18:50:59 -05:00
pub trait ToLua<'a> {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
2017-05-21 18:50:59 -05:00
fn to_lua(self, lua: &'a Lua) -> LuaResult<LuaValue<'a>>;
}
2017-06-17 20:23:17 -05:00
/// Trait for types convertible from `LuaValue`.
2017-05-21 18:50:59 -05:00
pub trait FromLua<'a>: Sized {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
2017-05-21 18:50:59 -05:00
fn from_lua(lua_value: LuaValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
}
2017-06-17 20:23:17 -05:00
/// Multiple Lua values used for both argument passing and also for multiple return values.
2017-05-21 18:50:59 -05:00
#[derive(Debug, Clone)]
pub struct LuaMultiValue<'lua>(VecDeque<LuaValue<'lua>>);
impl<'lua> LuaMultiValue<'lua> {
pub fn new() -> LuaMultiValue<'lua> {
LuaMultiValue(VecDeque::new())
}
}
impl<'lua> FromIterator<LuaValue<'lua>> for LuaMultiValue<'lua> {
fn from_iter<I: IntoIterator<Item = LuaValue<'lua>>>(iter: I) -> Self {
LuaMultiValue(VecDeque::from_iter(iter))
}
}
impl<'lua> IntoIterator for LuaMultiValue<'lua> {
type Item = LuaValue<'lua>;
type IntoIter = <VecDeque<LuaValue<'lua>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'lua> Deref for LuaMultiValue<'lua> {
type Target = VecDeque<LuaValue<'lua>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'lua> DerefMut for LuaMultiValue<'lua> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
2017-06-17 20:23:17 -05:00
/// Trait for types convertible to any number of Lua values.
///
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
/// This is a generalization of `ToLua`, allowing any number of resulting Lua
/// values instead of just one. Any type that implements `ToLua` will
/// automatically implement this trait.
2017-05-21 18:50:59 -05:00
pub trait ToLuaMulti<'a> {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
2017-05-21 18:50:59 -05:00
fn to_lua_multi(self, lua: &'a Lua) -> LuaResult<LuaMultiValue<'a>>;
}
2017-06-17 20:23:17 -05:00
/// Trait for types that can be created from an arbitrary number of Lua values.
///
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua
/// values to participate in the conversion. Any type that implements `FromLua`
/// will automatically implement this trait.
2017-05-21 18:50:59 -05:00
pub trait FromLuaMulti<'a>: Sized {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
///
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
/// In case `values` contains more values than needed to perform the
/// conversion, the excess values should be ignored. This reflects the
/// semantics of Lua when calling a function or assigning values. Similarly,
/// if not enough values are given, conversions should assume that any
/// missing values are nil.
2017-05-21 18:50:59 -05:00
fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
}
impl<'lua> ToLua<'lua> for LuaError {
fn to_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
unsafe {
push_wrapped_error(lua.state, self);
Ok(lua.pop_value(lua.state))
}
}
}
2017-06-15 09:26:39 -05:00
type LuaCallback = Box<
for<'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>,
>;
2017-05-21 18:50:59 -05:00
struct LuaRef<'lua> {
lua: &'lua Lua,
registry_id: c_int,
}
impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LuaRef({})", self.registry_id)
}
}
impl<'lua> Clone for LuaRef<'lua> {
fn clone(&self) -> Self {
unsafe {
self.lua.push_ref(self.lua.state, self);
self.lua.pop_ref(self.lua.state)
}
2017-05-21 18:50:59 -05:00
}
}
impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) {
unsafe {
ffi::luaL_unref(self.lua.state, ffi::LUA_REGISTRYINDEX, self.registry_id);
}
}
}
2017-06-17 20:23:17 -05:00
/// Type of Lua integer numbers.
pub type LuaInteger = ffi::lua_Integer;
2017-06-17 20:23:17 -05:00
/// Type of Lua floating point numbers.
pub type LuaNumber = ffi::lua_Number;
2017-06-17 20:23:17 -05:00
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
2017-06-17 20:23:17 -05:00
/// Handle to an internal Lua string.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
2017-05-21 18:50:59 -05:00
#[derive(Clone, Debug)]
pub struct LuaString<'lua>(LuaRef<'lua>);
impl<'lua> LuaString<'lua> {
2017-06-17 20:23:17 -05:00
/// Get a `&str` slice if the Lua string is valid UTF-8.
2017-06-22 03:49:18 -05:00
///
/// # Example
///
/// ```
/// # extern crate rlua;
/// # use rlua::*;
/// # fn main() {
/// let lua = Lua::new();
/// let globals = lua.globals().unwrap();
///
/// let version: LuaString = globals.get("_VERSION").unwrap();
/// assert!(version.to_str().unwrap().contains("Lua"));
///
/// let non_utf8: LuaString = lua.eval(r#" "test\xff" "#).unwrap();
/// assert!(non_utf8.to_str().is_err());
/// # }
/// ```
pub fn to_str(&self) -> LuaResult<&str> {
2017-05-21 18:50:59 -05:00
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 1)?;
lua.push_ref(lua.state, &self.0);
2017-05-21 18:50:59 -05:00
assert_eq!(ffi::lua_type(lua.state, -1), ffi::LUA_TSTRING);
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
let s = CStr::from_ptr(ffi::lua_tostring(lua.state, -1))
.to_str()
.map_err(|e| LuaConversionError::Utf8Error(e))?;
2017-05-21 18:50:59 -05:00
ffi::lua_pop(lua.state, 1);
Ok(s)
})
}
}
}
2017-06-17 20:23:17 -05:00
/// Handle to an internal Lua table.
2017-05-21 18:50:59 -05:00
#[derive(Clone, Debug)]
pub struct LuaTable<'lua>(LuaRef<'lua>);
impl<'lua> LuaTable<'lua> {
2017-06-17 20:23:17 -05:00
/// Sets a key-value pair in the table.
///
/// If the value is `nil`, this will effectively remove the pair.
///
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
/// This might invoke the `__newindex` metamethod. Use the `raw_set` method
/// if that is not desired.
2017-05-21 18:50:59 -05:00
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?;
2017-05-21 18:50:59 -05:00
unsafe {
error_guard(lua.state, 0, 0, |state| {
check_stack(state, 3)?;
lua.push_ref(state, &self.0);
lua.push_value(state, key);
lua.push_value(state, value);
ffi::lua_settable(state, -3);
Ok(())
})
}
}
2017-06-17 20:23:17 -05:00
/// Gets the value associated to `key` from the table.
///
/// If no value is associated to `key`, returns the `nil` value.
///
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
/// This might invoke the `__index` metamethod. Use the `raw_get` method if
/// that is not desired.
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
unsafe {
let res = error_guard(lua.state, 0, 0, |state| {
check_stack(state, 2)?;
lua.push_ref(state, &self.0);
lua.push_value(state, key.to_lua(lua)?);
ffi::lua_gettable(state, -2);
let res = lua.pop_value(state);
ffi::lua_pop(state, 1);
Ok(res)
})?;
V::from_lua(res, lua)
}
}
2017-06-17 20:23:17 -05:00
/// Checks whether the table contains a non-nil value for `key`.
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> LuaResult<bool> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
unsafe {
error_guard(lua.state, 0, 0, |state| {
check_stack(state, 2)?;
lua.push_ref(state, &self.0);
lua.push_value(state, key);
ffi::lua_gettable(state, -2);
let has = ffi::lua_isnil(state, -1) == 0;
ffi::lua_pop(state, 2);
Ok(has)
})
}
}
2017-06-17 20:23:17 -05:00
/// Sets a key-value pair without invoking metamethods.
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 3)?;
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?);
lua.push_value(lua.state, value.to_lua(lua)?);
ffi::lua_rawset(lua.state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_pop(lua.state, 1);
Ok(())
})
}
}
2017-06-17 20:23:17 -05:00
/// Gets the value associated to `key` without invoking metamethods.
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 2)?;
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?);
ffi::lua_gettable(lua.state, -2);
let res = V::from_lua(lua.pop_value(lua.state), lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res)
})
}
}
2017-06-17 20:23:17 -05:00
/// Returns the result of the Lua `#` operator.
///
2017-06-18 17:11:55 -05:00
/// This might invoke the `__len` metamethod. Use the `raw_len` method if
/// that is not desired.
pub fn len(&self) -> LuaResult<LuaInteger> {
2017-05-21 18:50:59 -05:00
let lua = self.0.lua;
unsafe {
error_guard(lua.state, 0, 0, |state| {
check_stack(state, 1)?;
lua.push_ref(state, &self.0);
Ok(ffi::luaL_len(state, -1))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Returns the result of the Lua `#` operator, without invoking the
/// `__len` metamethod.
pub fn raw_len(&self) -> LuaResult<LuaInteger> {
2017-05-21 18:50:59 -05:00
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 1)?;
lua.push_ref(lua.state, &self.0);
let len = ffi::lua_rawlen(lua.state, -1);
ffi::lua_pop(lua.state, 1);
Ok(len as LuaInteger)
2017-05-21 18:50:59 -05:00
})
}
}
/// Consume this table and return an iterator over the pairs of the table,
2017-06-19 02:08:50 -05:00
/// works like the Lua 'pairs' function.
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> LuaTablePairs<'lua, K, V> {
let next_key = Some(LuaRef {
lua: self.0.lua,
registry_id: ffi::LUA_REFNIL,
});
LuaTablePairs {
table: self.0,
next_key,
_phantom: PhantomData,
}
}
2017-05-21 18:50:59 -05:00
/// Consume this table and return an iterator over the values of this table,
/// which should be a sequence. Works like the Lua 'ipairs' function, but
/// doesn't return the indexes, only the values in order.
pub fn sequence_values<V: FromLua<'lua>>(self) -> LuaTableSequence<'lua, V> {
LuaTableSequence {
table: self.0,
index: Some(1),
_phantom: PhantomData,
2017-05-21 18:50:59 -05:00
}
}
}
2017-05-21 18:50:59 -05:00
/// An iterator over the pairs of a Lua table.
///
2017-06-19 02:08:50 -05:00
/// Should behave exactly like the lua 'pairs' function. Holds an internal
/// reference to the table.
pub struct LuaTablePairs<'lua, K, V> {
table: LuaRef<'lua>,
next_key: Option<LuaRef<'lua>>,
_phantom: PhantomData<(K, V)>,
}
2017-05-21 18:50:59 -05:00
impl<'lua, K, V> Iterator for LuaTablePairs<'lua, K, V>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
{
type Item = LuaResult<(K, V)>;
2017-05-21 18:50:59 -05:00
fn next(&mut self) -> Option<Self::Item> {
if let Some(next_key) = self.next_key.take() {
let lua = self.table.lua;
2017-05-21 18:50:59 -05:00
unsafe {
if let Err(e) = check_stack(lua.state, 4) {
return Some(Err(e));
}
2017-05-21 18:50:59 -05:00
lua.push_ref(lua.state, &self.table);
lua.push_ref(lua.state, &next_key);
match error_guard(lua.state, 2, 0, |state| if ffi::lua_next(state, -2) != 0 {
ffi::lua_pushvalue(state, -2);
let key = lua.pop_value(state);
let value = lua.pop_value(state);
let next_key = lua.pop_ref(lua.state);
ffi::lua_pop(lua.state, 1);
Ok(Some((key, value, next_key)))
} else {
ffi::lua_pop(lua.state, 1);
Ok(None)
}) {
Ok(Some((key, value, next_key))) => {
self.next_key = Some(next_key);
Some((|| {
let key = K::from_lua(key, lua)?;
let value = V::from_lua(value, lua)?;
Ok((key, value))
})())
2017-05-21 18:50:59 -05:00
}
Ok(None) => None,
Err(e) => Some(Err(e)),
2017-05-21 18:50:59 -05:00
}
}
} else {
None
2017-05-21 18:50:59 -05:00
}
}
}
2017-05-26 22:49:12 -05:00
/// An iterator over the sequence part of a Lua table.
///
/// Should behave similarly to the lua 'ipairs" function, except only produces
/// the values, not the indexes. Holds an internal reference to the table.
pub struct LuaTableSequence<'lua, V> {
table: LuaRef<'lua>,
index: Option<LuaInteger>,
_phantom: PhantomData<V>,
}
impl<'lua, V> Iterator for LuaTableSequence<'lua, V>
where
V: FromLua<'lua>,
{
type Item = LuaResult<V>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(index) = self.index.take() {
let lua = self.table.lua;
unsafe {
if let Err(e) = check_stack(lua.state, 2) {
return Some(Err(e));
}
2017-05-26 22:49:12 -05:00
lua.push_ref(lua.state, &self.table);
match error_guard(
lua.state,
1,
0,
|state| if ffi::lua_geti(state, -1, index) != ffi::LUA_TNIL {
let value = lua.pop_value(state);
ffi::lua_pop(state, 1);
Ok(Some(value))
} else {
ffi::lua_pop(state, 2);
Ok(None)
},
) {
Ok(Some(r)) => {
self.index = Some(index + 1);
Some(V::from_lua(r, lua))
}
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}
} else {
None
}
2017-05-26 22:49:12 -05:00
}
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Handle to an internal Lua function.
2017-05-21 18:50:59 -05:00
#[derive(Clone, Debug)]
pub struct LuaFunction<'lua>(LuaRef<'lua>);
impl<'lua> LuaFunction<'lua> {
2017-06-17 20:23:17 -05:00
/// Calls the function, passing `args` as function arguments.
///
/// The function's return values are converted to the generic type `R`.
2017-05-21 18:50:59 -05:00
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> LuaResult<R> {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
check_stack(lua.state, nargs + 3)?;
2017-05-21 18:50:59 -05:00
let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(lua.state, &self.0);
2017-05-21 18:50:59 -05:00
for arg in args {
lua.push_value(lua.state, arg);
2017-05-21 18:50:59 -05:00
}
2017-06-15 09:26:39 -05:00
handle_error(
lua.state,
pcall_with_traceback(lua.state, nargs, ffi::LUA_MULTRET),
)?;
2017-05-21 18:50:59 -05:00
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = LuaMultiValue::new();
for _ in 0..nresults {
results.push_front(lua.pop_value(lua.state));
2017-05-21 18:50:59 -05:00
}
R::from_lua_multi(results, lua)
})
}
}
2017-06-17 20:23:17 -05:00
/// Returns a function that, when called with no arguments, calls `self`, passing `args` as
/// arguments.
///
/// This is equivalent to this Lua code:
///
/// ```notrust
/// function bind(f, ...)
/// return function() f(...) end
/// end
/// ```
2017-06-21 17:38:08 -05:00
///
/// # Example
///
/// ```
/// # extern crate rlua;
/// # use rlua::*;
///
/// # fn main() {
/// let lua = Lua::new();
/// let globals = lua.globals().unwrap();
///
/// // Bind the argument `123` to Lua's `tostring` function
/// let tostring: LuaFunction = globals.get("tostring").unwrap();
/// let tostring_123: LuaFunction = tostring.bind(123i32).unwrap();
///
/// // Now we can call `tostring_123` without arguments to get the result of `tostring(123)`
/// let result: String = tostring_123.call(()).unwrap();
/// assert_eq!(result, "123");
/// # }
/// ```
2017-05-21 18:50:59 -05:00
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> LuaResult<LuaFunction<'lua>> {
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
check_stack(state, nbinds + 1).expect("not enough space to handle bound arguments");
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
ffi::lua_insert(state, 1);
// TODO: This is quadratic
for i in 0..nbinds {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
ffi::lua_insert(state, i + 2);
}
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
ffi::lua_gettop(state)
}
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
check_stack(lua.state, nargs + 2)?;
lua.push_ref(lua.state, &self.0);
2017-05-21 18:50:59 -05:00
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(lua.state, arg);
2017-05-21 18:50:59 -05:00
}
ffi::lua_pushcclosure(lua.state, bind_call_impl, nargs + 2);
Ok(LuaFunction(lua.pop_ref(lua.state)))
2017-05-21 18:50:59 -05:00
})
}
}
}
2017-06-17 20:23:17 -05:00
/// Status of a Lua thread (or coroutine).
///
/// A `LuaThread` is `Active` before the coroutine function finishes, Dead after
/// it finishes, and in Error state if error has been called inside the
/// coroutine.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum LuaThreadStatus {
2017-06-17 20:23:17 -05:00
/// The thread has finished executing.
Dead,
2017-06-17 20:23:17 -05:00
/// The thread is currently running or suspended because it has called `coroutine.yield`.
Active,
2017-06-17 20:23:17 -05:00
/// The thread has thrown an error during execution.
Error,
}
2017-06-17 20:23:17 -05:00
/// Handle to an internal Lua thread (or coroutine).
#[derive(Clone, Debug)]
pub struct LuaThread<'lua>(LuaRef<'lua>);
impl<'lua> LuaThread<'lua> {
2017-06-17 20:23:17 -05:00
/// Resumes execution of this thread.
///
/// Equivalent to `coroutine.resume`.
///
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
/// are passed to its main function.
///
/// If the thread is no longer in `Active` state (meaning it has finished execution or
2017-06-17 22:50:40 -05:00
/// encountered an error), this will return Err(CoroutineInactive),
/// otherwise will return Ok as follows:
2017-06-17 20:23:17 -05:00
///
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
/// `return`s values from its main function, returns those.
///
/// # Example
///
/// ```
/// # extern crate rlua;
/// # use rlua::*;
///
/// # fn main() {
/// let lua = Lua::new();
/// let thread: LuaThread = lua.eval(r#"
/// coroutine.create(function(arg)
/// assert(arg == 42)
/// local yieldarg = coroutine.yield(123)
/// assert(yieldarg == 43)
/// return 987
/// end)
/// "#).unwrap();
///
/// assert_eq!(thread.resume::<_, u32>(42).unwrap(), 123);
/// assert_eq!(thread.resume::<_, u32>(43).unwrap(), 987);
///
/// // The coroutine has now returned, so `resume` will fail
/// match thread.resume::<_, u32>(()) {
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
/// Err(LuaError::CoroutineInactive) => {},
/// unexpected => panic!("unexpected result {:?}", unexpected),
/// }
/// # }
/// ```
2017-06-17 22:50:40 -05:00
pub fn resume<A, R>(&self, args: A) -> LuaResult<R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 1)?;
lua.push_ref(lua.state, &self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
let status = ffi::lua_status(thread_state);
2017-06-17 22:50:40 -05:00
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
return Err(LuaError::CoroutineInactive);
2017-06-17 22:50:40 -05:00
}
2017-06-17 22:50:40 -05:00
ffi::lua_pop(lua.state, 1);
2017-06-17 22:50:40 -05:00
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
check_stack(thread_state, nargs)?;
2017-06-17 22:50:40 -05:00
for arg in args {
lua.push_value(thread_state, arg);
2017-06-17 22:50:40 -05:00
}
2017-06-17 22:50:40 -05:00
handle_error(
lua.state,
resume_with_traceback(thread_state, lua.state, nargs),
)?;
let nresults = ffi::lua_gettop(thread_state);
let mut results = LuaMultiValue::new();
for _ in 0..nresults {
results.push_front(lua.pop_value(thread_state));
}
2017-06-17 22:50:40 -05:00
R::from_lua_multi(results, lua)
})
}
}
2017-06-17 20:23:17 -05:00
/// Gets the status of the thread.
pub fn status(&self) -> LuaResult<LuaThreadStatus> {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 1)?;
lua.push_ref(lua.state, &self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
Ok(LuaThreadStatus::Error)
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
Ok(LuaThreadStatus::Active)
} else {
Ok(LuaThreadStatus::Dead)
}
})
}
}
}
2017-06-17 20:23:17 -05:00
/// Kinds of metamethods that can be overridden.
2017-05-21 18:50:59 -05:00
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum LuaMetaMethod {
2017-06-17 20:23:17 -05:00
/// The `+` operator.
2017-05-21 18:50:59 -05:00
Add,
2017-06-17 20:23:17 -05:00
/// The `-` operator.
2017-05-21 18:50:59 -05:00
Sub,
2017-06-17 20:23:17 -05:00
/// The `*` operator.
2017-05-21 18:50:59 -05:00
Mul,
2017-06-17 20:23:17 -05:00
/// The `/` operator.
2017-05-21 18:50:59 -05:00
Div,
2017-06-17 20:23:17 -05:00
/// The `%` operator.
2017-05-21 18:50:59 -05:00
Mod,
2017-06-17 20:23:17 -05:00
/// The `^` operator.
2017-05-21 18:50:59 -05:00
Pow,
2017-06-17 20:23:17 -05:00
/// The unary minus (`-`) operator.
2017-05-21 18:50:59 -05:00
Unm,
/// The floor division (//) operator.
IDiv,
/// The bitwise AND (&) operator.
BAnd,
/// The bitwise OR (|) operator.
BOr,
/// The bitwise XOR (binary ~) operator.
BXor,
/// The bitwise NOT (unary ~) operator.
BNot,
/// The bitwise left shift (<<) operator.
Shl,
/// The bitwise right shift (>>) operator.
Shr,
2017-06-17 20:23:17 -05:00
/// The string concatenation operator `..`.
2017-05-21 18:50:59 -05:00
Concat,
2017-06-17 20:23:17 -05:00
/// The length operator `#`.
2017-05-21 18:50:59 -05:00
Len,
2017-06-17 20:23:17 -05:00
/// The `==` operator.
2017-05-21 18:50:59 -05:00
Eq,
2017-06-17 20:23:17 -05:00
/// The `<` operator.
2017-05-21 18:50:59 -05:00
Lt,
2017-06-17 20:23:17 -05:00
/// The `<=` operator.
2017-05-21 18:50:59 -05:00
Le,
2017-06-17 20:23:17 -05:00
/// Index access `obj[key]`.
2017-05-21 18:50:59 -05:00
Index,
2017-06-17 20:23:17 -05:00
/// Index write access `obj[key] = value`.
2017-05-21 18:50:59 -05:00
NewIndex,
2017-06-17 20:23:17 -05:00
/// The call "operator" `obj(arg1, args2, ...)`.
2017-05-21 18:50:59 -05:00
Call,
/// tostring(ud) will call this if it exists
ToString,
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Stores methods of a userdata object.
///
/// Methods added will be added to the `__index` table on the metatable for the
/// userdata, so they can be called as `userdata:method(args)` as expected. If
/// there are any regular methods, and an `Index` metamethod is given, it will
/// be called as a *fallback* if the index doesn't match an existing regular
/// method.
2017-05-21 18:50:59 -05:00
pub struct LuaUserDataMethods<T> {
methods: HashMap<String, LuaCallback>,
meta_methods: HashMap<LuaMetaMethod, LuaCallback>,
_type: PhantomData<T>,
}
impl<T: LuaUserDataType> LuaUserDataMethods<T> {
/// Add a regular method as a function which accepts a &T as the first
/// parameter.
2017-05-21 18:50:59 -05:00
pub fn add_method<M>(&mut self, name: &str, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
2017-06-15 09:26:39 -05:00
self.methods.insert(
name.to_owned(),
Self::box_method(method),
);
2017-05-21 18:50:59 -05:00
}
/// Add a regular method as a function which accepts a &mut T as the first
/// parameter.
2017-05-21 18:50:59 -05:00
pub fn add_method_mut<M>(&mut self, name: &str, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
2017-06-15 09:26:39 -05:00
self.methods.insert(
name.to_owned(),
Self::box_method_mut(method),
);
2017-05-21 18:50:59 -05:00
}
/// Add a regular method as a function which accepts generic arguments, the
/// first argument will always be a LuaUserData of type T.
2017-05-21 18:50:59 -05:00
pub fn add_function<F>(&mut self, name: &str, function: F)
where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.methods.insert(name.to_owned(), Box::new(function));
}
/// Add a metamethod as a function which accepts a &T as the first
/// parameter. This can cause an error with certain binary metamethods that
/// can trigger if ony the right side has a metatable.
2017-05-21 18:50:59 -05:00
pub fn add_meta_method<M>(&mut self, meta: LuaMetaMethod, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.meta_methods.insert(meta, Self::box_method(method));
}
/// Add a metamethod as a function which accepts a &mut T as the first
/// parameter. This can cause an error with certain binary metamethods that
/// can trigger if ony the right side has a metatable.
2017-05-21 18:50:59 -05:00
pub fn add_meta_method_mut<M>(&mut self, meta: LuaMetaMethod, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.meta_methods.insert(meta, Self::box_method_mut(method));
}
/// Add a metamethod as a function which accepts generic arguments.
/// Metamethods in Lua for binary operators can be triggered if either the
/// left or right argument to the binary operator has a metatable, so the
/// first argument here is not necessarily a userdata of type T.
2017-05-21 18:50:59 -05:00
pub fn add_meta_function<F>(&mut self, meta: LuaMetaMethod, function: F)
where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.meta_methods.insert(meta, Box::new(function));
}
fn box_method<M>(mut method: M) -> LuaCallback
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
2017-06-15 09:26:39 -05:00
let userdata = LuaUserData::from_lua(front, lua)?;
let userdata = userdata.borrow::<T>()?;
method(lua, &userdata, args)
} else {
Err(
LuaConversionError::FromLua(
"No userdata supplied as first argument to method".to_owned(),
).into(),
)
2017-06-15 09:26:39 -05:00
})
2017-05-21 18:50:59 -05:00
}
fn box_method_mut<M>(mut method: M) -> LuaCallback
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
2017-06-15 09:26:39 -05:00
let userdata = LuaUserData::from_lua(front, lua)?;
let mut userdata = userdata.borrow_mut::<T>()?;
method(lua, &mut userdata, args)
} else {
Err(
LuaConversionError::FromLua(
"No userdata supplied as first argument to method".to_owned(),
).into(),
)
2017-06-15 09:26:39 -05:00
})
2017-05-21 18:50:59 -05:00
}
}
2017-06-17 20:23:17 -05:00
/// Trait for custom userdata types.
2017-05-21 18:50:59 -05:00
pub trait LuaUserDataType: 'static + Sized {
2017-06-17 20:23:17 -05:00
/// Adds custom methods and operators specific to this userdata.
2017-05-21 18:50:59 -05:00
fn add_methods(_methods: &mut LuaUserDataMethods<Self>) {}
}
/// Handle to an internal instance of custom userdata. All userdata in this API
2017-06-17 20:23:17 -05:00
/// is based around `RefCell`, to best match the mutable semantics of the Lua
/// language.
2017-05-21 18:50:59 -05:00
#[derive(Clone, Debug)]
pub struct LuaUserData<'lua>(LuaRef<'lua>);
impl<'lua> LuaUserData<'lua> {
2017-06-17 20:23:17 -05:00
/// Checks whether `T` is the type of this userdata.
2017-05-21 18:50:59 -05:00
pub fn is<T: LuaUserDataType>(&self) -> bool {
self.inspect(|_: &RefCell<T>| Ok(())).is_ok()
}
2017-05-21 21:32:16 -05:00
/// Borrow this userdata out of the internal RefCell that is held in lua.
2017-05-21 18:50:59 -05:00
pub fn borrow<T: LuaUserDataType>(&self) -> LuaResult<Ref<T>> {
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
self.inspect(|cell| {
Ok(
cell.try_borrow().map_err(|_| LuaUserDataError::BorrowError)?,
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
)
})
2017-05-21 18:50:59 -05:00
}
2017-05-21 21:32:16 -05:00
/// Borrow mutably this userdata out of the internal RefCell that is held in lua.
2017-05-21 18:50:59 -05:00
pub fn borrow_mut<T: LuaUserDataType>(&self) -> LuaResult<RefMut<T>> {
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
self.inspect(|cell| {
Ok(cell.try_borrow_mut().map_err(
|_| LuaUserDataError::BorrowError,
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
)?)
})
2017-05-21 18:50:59 -05:00
}
fn inspect<'a, T, R, F>(&'a self, func: F) -> LuaResult<R>
2017-06-15 09:26:39 -05:00
where
T: LuaUserDataType,
F: FnOnce(&'a RefCell<T>) -> LuaResult<R>,
2017-05-21 18:50:59 -05:00
{
unsafe {
let lua = self.0.lua;
stack_guard(lua.state, 0, move || {
check_stack(lua.state, 3)?;
lua.push_ref(lua.state, &self.0);
2017-05-21 18:50:59 -05:00
let userdata = ffi::lua_touserdata(lua.state, -1);
assert!(!userdata.is_null());
2017-05-21 18:50:59 -05:00
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return Err(LuaUserDataError::TypeMismatch.into());
2017-05-21 18:50:59 -05:00
}
2017-06-15 09:26:39 -05:00
ffi::lua_rawgeti(
lua.state,
ffi::LUA_REGISTRYINDEX,
lua.userdata_metatable::<T>()? as ffi::lua_Integer,
);
2017-05-21 18:50:59 -05:00
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
return Err(LuaUserDataError::TypeMismatch.into());
2017-05-21 18:50:59 -05:00
}
let res = func(&*(userdata as *const RefCell<T>));
ffi::lua_pop(lua.state, 3);
res
})
}
}
}
2017-06-17 20:23:17 -05:00
/// Top level Lua struct which holds the Lua state itself.
2017-05-21 18:50:59 -05:00
pub struct Lua {
state: *mut ffi::lua_State,
main_state: *mut ffi::lua_State,
2017-05-21 18:50:59 -05:00
ephemeral: bool,
}
impl Drop for Lua {
fn drop(&mut self) {
unsafe {
if !self.ephemeral {
ffi::lua_close(self.state);
2017-05-21 18:50:59 -05:00
}
}
}
}
impl Lua {
2017-06-17 20:23:17 -05:00
/// Creates a new Lua state.
///
/// Also loads the standard library.
2017-05-21 18:50:59 -05:00
pub fn new() -> Lua {
unsafe {
let state = ffi::luaL_newstate();
ffi::luaL_openlibs(state);
stack_guard(state, 0, || {
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
state,
&LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void,
);
let registered_userdata = ffi::lua_newuserdata(
state,
mem::size_of::<RefCell<HashMap<TypeId, c_int>>>(),
) as *mut RefCell<HashMap<TypeId, c_int>>;
2017-05-21 18:50:59 -05:00
ptr::write(registered_userdata, RefCell::new(HashMap::new()));
ffi::lua_newtable(state);
push_string(state, "__gc");
ffi::lua_pushcfunction(state, destructor::<RefCell<HashMap<TypeId, c_int>>>);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_setmetatable(state, -2);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
2017-05-21 18:50:59 -05:00
Ok(())
}).unwrap();
2017-05-21 18:50:59 -05:00
stack_guard(state, 0, || {
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
2017-05-21 18:50:59 -05:00
ffi::lua_newtable(state);
push_string(state, "__gc");
ffi::lua_pushcfunction(state, destructor::<LuaCallback>);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
push_string(state, "__metatable");
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
2017-05-21 18:50:59 -05:00
Ok(())
}).unwrap();
2017-05-21 18:50:59 -05:00
stack_guard(state, 0, || {
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
push_string(state, "pcall");
ffi::lua_pushcfunction(state, safe_pcall);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
push_string(state, "xpcall");
ffi::lua_pushcfunction(state, safe_xpcall);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_pop(state, 1);
Ok(())
}).unwrap();
2017-05-21 18:50:59 -05:00
Lua {
state,
main_state: state,
2017-05-21 18:50:59 -05:00
ephemeral: false,
}
}
}
/// Loads a chunk of Lua code and returns it as a function.
2017-06-17 20:23:17 -05:00
///
/// The source can be named by setting the `name` parameter. This is
/// generally recommended as it results in better error traces.
2017-06-17 20:23:17 -05:00
///
/// Equivalent to Lua's `load` function.
pub fn load(&self, source: &str, name: Option<&str>) -> LuaResult<LuaFunction> {
2017-05-21 18:50:59 -05:00
unsafe {
stack_guard(self.state, 0, || {
2017-06-15 09:26:39 -05:00
handle_error(
self.state,
if let Some(name) = name {
let name = CString::new(name.to_owned()).map_err(|e| {
LuaConversionError::NulError(e)
})?;
2017-06-15 09:26:39 -05:00
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
name.as_ptr(),
)
} else {
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
)
},
)?;
2017-05-21 18:50:59 -05:00
Ok(LuaFunction(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
})
}
}
/// 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, R: FromLuaMulti<'lua>>(
&'lua self,
source: &str,
name: Option<&str>,
) -> LuaResult<R> {
self.load(source, name)?.call(())
}
2017-06-17 20:23:17 -05:00
/// 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).
2017-05-21 18:50:59 -05:00
pub fn eval<'lua, R: FromLuaMulti<'lua>>(&'lua self, source: &str) -> LuaResult<R> {
unsafe {
stack_guard(self.state, 0, || {
// 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.
2017-05-21 18:50:59 -05:00
let return_source = "return ".to_owned() + source;
2017-06-15 09:26:39 -05:00
let mut res = ffi::luaL_loadbuffer(
self.state,
return_source.as_ptr() as *const c_char,
return_source.len(),
ptr::null(),
);
2017-05-21 18:50:59 -05:00
if res == ffi::LUA_ERRSYNTAX {
ffi::lua_pop(self.state, 1);
2017-06-15 09:26:39 -05:00
res = ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
);
2017-05-21 18:50:59 -05:00
}
handle_error(self.state, res)?;
LuaFunction(self.pop_ref(self.state)).call(())
})
}
}
2017-06-17 20:23:17 -05:00
/// Pass a `&str` slice to Lua, creating and returning a interned Lua string.
2017-05-21 18:50:59 -05:00
pub fn create_string(&self, s: &str) -> LuaResult<LuaString> {
unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1)?;
ffi::lua_pushlstring(self.state, s.as_ptr() as *const c_char, s.len());
Ok(LuaString(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Creates and returns a new table.
pub fn create_table(&self) -> LuaResult<LuaTable> {
2017-05-21 18:50:59 -05:00
unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1)?;
ffi::lua_newtable(self.state);
Ok(LuaTable(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Creates a table and fills it with values from an iterator.
pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> LuaResult<LuaTable>
2017-06-15 09:26:39 -05:00
where
K: ToLua<'lua>,
V: ToLua<'lua>,
I: IntoIterator<Item = (K, V)>,
2017-05-21 18:50:59 -05:00
{
unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 3)?;
ffi::lua_newtable(self.state);
for (k, v) in cont {
self.push_value(self.state, k.to_lua(self)?);
self.push_value(self.state, v.to_lua(self)?);
ffi::lua_rawset(self.state, -3);
}
Ok(LuaTable(self.pop_ref(self.state)))
})
2017-05-21 18:50:59 -05:00
}
}
2017-06-17 20:23:17 -05:00
/// Creates a table from an iterator of values, using `1..` as the keys.
pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> LuaResult<LuaTable>
2017-06-15 09:26:39 -05:00
where
T: ToLua<'lua>,
I: IntoIterator<Item = T>,
2017-05-21 18:50:59 -05:00
{
self.create_table_from(cont.into_iter().enumerate().map(|(k, v)| (k + 1, v)))
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
2017-05-21 18:50:59 -05:00
pub fn create_function<F>(&self, func: F) -> LuaResult<LuaFunction>
2017-06-15 09:26:39 -05:00
where
F: 'static + for<'a> FnMut(&'a Lua, LuaMultiValue<'a>) -> LuaResult<LuaMultiValue<'a>>,
2017-05-21 18:50:59 -05:00
{
self.create_callback_function(Box::new(func))
}
2017-06-17 20:23:17 -05:00
/// Wraps a Lua function into a new thread (or coroutine).
///
/// Equivalent to `coroutine.create`.
pub fn create_thread<'lua>(&'lua self, func: LuaFunction<'lua>) -> LuaResult<LuaThread<'lua>> {
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 1)?;
let thread_state = ffi::lua_newthread(self.state);
self.push_ref(thread_state, &func.0);
Ok(LuaThread(self.pop_ref(self.state)))
})
}
}
2017-06-17 20:23:17 -05:00
/// Create a Lua userdata object from a custom userdata type.
2017-05-21 18:50:59 -05:00
pub fn create_userdata<T>(&self, data: T) -> LuaResult<LuaUserData>
2017-06-15 09:26:39 -05:00
where
T: LuaUserDataType,
2017-05-21 18:50:59 -05:00
{
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 2)?;
let data = RefCell::new(data);
2017-06-15 09:26:39 -05:00
let data_userdata =
ffi::lua_newuserdata(self.state, mem::size_of::<RefCell<T>>()) as
*mut RefCell<T>;
2017-05-21 18:50:59 -05:00
ptr::write(data_userdata, data);
2017-06-15 09:26:39 -05:00
ffi::lua_rawgeti(
self.state,
ffi::LUA_REGISTRYINDEX,
self.userdata_metatable::<T>()? as ffi::lua_Integer,
);
2017-05-21 18:50:59 -05:00
ffi::lua_setmetatable(self.state, -2);
Ok(LuaUserData(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Returns a handle to the global environment.
pub fn globals(&self) -> LuaResult<LuaTable> {
2017-05-21 18:50:59 -05:00
unsafe {
check_stack(self.state, 1)?;
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
Ok(LuaTable(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
}
}
2017-06-17 20:23:17 -05:00
/// Coerces a Lua value to a string.
///
/// The value must be a string (in which case this is a no-op) or a number.
2017-05-21 18:50:59 -05:00
pub fn coerce_string<'lua>(&'lua self, v: LuaValue<'lua>) -> LuaResult<LuaString<'lua>> {
match v {
LuaValue::String(s) => Ok(s),
v => unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1)?;
self.push_value(self.state, v);
2017-05-21 18:50:59 -05:00
if ffi::lua_tostring(self.state, -1).is_null() {
Err(
LuaConversionError::FromLua(
"cannot convert lua value to string".to_owned(),
).into(),
)
2017-05-21 18:50:59 -05:00
} else {
Ok(LuaString(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
}
})
},
}
}
2017-06-17 20:23:17 -05:00
/// Coerces a Lua value to an integer.
///
/// The value must be an integer, or a floating point number or a string that can be converted
/// to an integer. Refer to the Lua manual for details.
2017-05-21 18:50:59 -05:00
pub fn coerce_integer(&self, v: LuaValue) -> LuaResult<LuaInteger> {
match v {
LuaValue::Integer(i) => Ok(i),
v => unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1)?;
self.push_value(self.state, v);
2017-05-21 18:50:59 -05:00
let mut isint = 0;
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
if isint == 0 {
Err(
LuaConversionError::FromLua(
"cannot convert lua value to integer".to_owned(),
).into(),
)
2017-05-21 18:50:59 -05:00
} else {
ffi::lua_pop(self.state, 1);
Ok(i)
}
})
},
}
}
2017-06-17 20:23:17 -05:00
/// Coerce a Lua value to a number.
///
/// The value must be a number or a string that can be converted to a number. Refer to the Lua
/// manual for details.
2017-05-21 18:50:59 -05:00
pub fn coerce_number(&self, v: LuaValue) -> LuaResult<LuaNumber> {
match v {
LuaValue::Number(n) => Ok(n),
v => unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1)?;
self.push_value(self.state, v);
2017-05-21 18:50:59 -05:00
let mut isnum = 0;
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
if isnum == 0 {
Err(
LuaConversionError::FromLua(
"cannot convert lua value to number".to_owned(),
).into(),
)
2017-05-21 18:50:59 -05:00
} else {
ffi::lua_pop(self.state, 1);
Ok(n)
}
})
},
}
}
pub fn from<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> LuaResult<LuaValue<'lua>> {
2017-05-21 18:50:59 -05:00
t.to_lua(self)
}
pub fn to<'lua, T: FromLua<'lua>>(&'lua self, value: LuaValue<'lua>) -> LuaResult<T> {
2017-05-21 18:50:59 -05:00
T::from_lua(value, self)
}
2017-06-17 20:23:17 -05:00
/// Packs up a value that implements `ToLuaMulti` into a `LuaMultiValue` instance.
///
/// This can be used to return arbitrary Lua values from a Rust function back to Lua.
2017-05-21 18:50:59 -05:00
pub fn pack<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> LuaResult<LuaMultiValue<'lua>> {
t.to_lua_multi(self)
}
2017-06-17 20:23:17 -05:00
/// Unpacks a `LuaMultiValue` instance into a value that implements `FromLuaMulti`.
///
/// This can be used to convert the arguments of a Rust function called by Lua.
2017-06-15 09:26:39 -05:00
pub fn unpack<'lua, T: FromLuaMulti<'lua>>(
&'lua self,
value: LuaMultiValue<'lua>,
) -> LuaResult<T> {
2017-05-21 18:50:59 -05:00
T::from_lua_multi(value, self)
}
fn create_callback_function(&self, func: LuaCallback) -> LuaResult<LuaFunction> {
unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || {
let lua = Lua {
state: state,
main_state: main_state(state),
2017-05-21 18:50:59 -05:00
ephemeral: true,
};
let func = &mut *(ffi::lua_touserdata(state, ffi::lua_upvalueindex(1)) as
2017-06-15 09:26:39 -05:00
*mut LuaCallback);
2017-05-21 18:50:59 -05:00
let nargs = ffi::lua_gettop(state);
let mut args = LuaMultiValue::new();
for _ in 0..nargs {
args.push_front(lua.pop_value(state));
2017-05-21 18:50:59 -05:00
}
let results = func(&lua, args)?;
let nresults = results.len() as c_int;
for r in results {
lua.push_value(state, r);
2017-05-21 18:50:59 -05:00
}
Ok(nresults)
})
}
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 2)?;
2017-06-15 09:26:39 -05:00
let func_userdata =
ffi::lua_newuserdata(self.state, mem::size_of::<LuaCallback>()) as
*mut LuaCallback;
2017-05-21 18:50:59 -05:00
ptr::write(func_userdata, func);
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
self.state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
2017-05-21 18:50:59 -05:00
ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX);
ffi::lua_setmetatable(self.state, -2);
ffi::lua_pushcclosure(self.state, callback_call_impl, 1);
Ok(LuaFunction(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
})
}
}
unsafe fn push_value(&self, state: *mut ffi::lua_State, value: LuaValue) {
match value {
LuaValue::Nil => {
ffi::lua_pushnil(state);
}
LuaValue::Boolean(b) => {
ffi::lua_pushboolean(state, if b { 1 } else { 0 });
}
LuaValue::LightUserData(ud) => {
ffi::lua_pushlightuserdata(state, ud.0);
}
LuaValue::Integer(i) => {
ffi::lua_pushinteger(state, i);
}
LuaValue::Number(n) => {
ffi::lua_pushnumber(state, n);
}
LuaValue::String(s) => {
self.push_ref(state, &s.0);
}
LuaValue::Table(t) => {
self.push_ref(state, &t.0);
}
LuaValue::Function(f) => {
self.push_ref(state, &f.0);
}
LuaValue::UserData(ud) => {
self.push_ref(state, &ud.0);
}
LuaValue::Thread(t) => {
self.push_ref(state, &t.0);
}
}
}
unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> LuaValue {
match ffi::lua_type(state, -1) {
ffi::LUA_TNIL => {
ffi::lua_pop(state, 1);
LuaNil
}
ffi::LUA_TBOOLEAN => {
let b = LuaValue::Boolean(ffi::lua_toboolean(state, -1) != 0);
ffi::lua_pop(state, 1);
b
}
ffi::LUA_TLIGHTUSERDATA => {
let ud = LuaValue::LightUserData(LightUserData(ffi::lua_touserdata(state, -1)));
ffi::lua_pop(state, 1);
ud
}
ffi::LUA_TNUMBER => {
if ffi::lua_isinteger(state, -1) != 0 {
let i = LuaValue::Integer(ffi::lua_tointeger(state, -1));
ffi::lua_pop(state, 1);
i
} else {
let n = LuaValue::Number(ffi::lua_tonumber(state, -1));
ffi::lua_pop(state, 1);
n
}
}
ffi::LUA_TSTRING => LuaValue::String(LuaString(self.pop_ref(state))),
ffi::LUA_TTABLE => LuaValue::Table(LuaTable(self.pop_ref(state))),
ffi::LUA_TFUNCTION => LuaValue::Function(LuaFunction(self.pop_ref(state))),
ffi::LUA_TUSERDATA => LuaValue::UserData(LuaUserData(self.pop_ref(state))),
ffi::LUA_TTHREAD => LuaValue::Thread(LuaThread(self.pop_ref(state))),
_ => panic!("LUA_TNONE in pop_value"),
}
}
unsafe fn push_ref(&self, state: *mut ffi::lua_State, lref: &LuaRef) {
2017-06-15 09:26:39 -05:00
assert_eq!(
lref.lua.main_state,
self.main_state,
"Lua instance passed LuaValue created from a different Lua"
);
ffi::lua_rawgeti(
state,
ffi::LUA_REGISTRYINDEX,
lref.registry_id as ffi::lua_Integer,
);
}
// Pops the topmost element of the stack and stores a reference to it in the
// registry.
//
// This pins the object, preventing garbage collection until the returned
// `LuaRef` is dropped.
unsafe fn pop_ref(&self, state: *mut ffi::lua_State) -> LuaRef {
let registry_id = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
LuaRef {
lua: self,
registry_id: registry_id,
}
}
2017-05-21 18:50:59 -05:00
unsafe fn userdata_metatable<T: LuaUserDataType>(&self) -> LuaResult<c_int> {
// Used if both an __index metamethod is set and regular methods, checks methods table
// first, then __index metamethod.
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushvalue(state, -1);
ffi::lua_gettable(state, ffi::lua_upvalueindex(1));
if ffi::lua_isnil(state, -1) == 0 {
ffi::lua_insert(state, -3);
ffi::lua_pop(state, 2);
1
} else {
ffi::lua_pop(state, 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(2));
ffi::lua_insert(state, -3);
ffi::lua_call(state, 2, 1);
1
}
}
stack_guard(self.state, 0, move || {
check_stack(self.state, 3)?;
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
self.state,
&LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void,
);
2017-05-21 18:50:59 -05:00
ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX);
let registered_userdata = ffi::lua_touserdata(self.state, -1) as
2017-06-15 09:26:39 -05:00
*mut RefCell<HashMap<TypeId, c_int>>;
2017-05-21 18:50:59 -05:00
let mut map = (*registered_userdata).borrow_mut();
ffi::lua_pop(self.state, 1);
match map.entry(TypeId::of::<T>()) {
HashMapEntry::Occupied(entry) => Ok(*entry.get()),
HashMapEntry::Vacant(entry) => {
ffi::lua_newtable(self.state);
let mut methods = LuaUserDataMethods {
methods: HashMap::new(),
meta_methods: HashMap::new(),
_type: PhantomData,
};
T::add_methods(&mut methods);
let has_methods = !methods.methods.is_empty();
if has_methods {
push_string(self.state, "__index");
ffi::lua_newtable(self.state);
check_stack(self.state, methods.methods.len() as c_int * 2)?;
for (k, m) in methods.methods {
push_string(self.state, &k);
2017-06-15 09:26:39 -05:00
self.push_value(
self.state,
LuaValue::Function(self.create_callback_function(m)?),
);
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
}
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
}
check_stack(self.state, methods.meta_methods.len() as c_int * 2)?;
for (k, m) in methods.meta_methods {
if k == LuaMetaMethod::Index && has_methods {
push_string(self.state, "__index");
ffi::lua_pushvalue(self.state, -1);
ffi::lua_gettable(self.state, -3);
2017-06-15 09:26:39 -05:00
self.push_value(
self.state,
LuaValue::Function(self.create_callback_function(m)?),
);
2017-05-21 18:50:59 -05:00
ffi::lua_pushcclosure(self.state, meta_index_impl, 2);
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
} else {
let name = match k {
LuaMetaMethod::Add => "__add",
LuaMetaMethod::Sub => "__sub",
LuaMetaMethod::Mul => "__mul",
LuaMetaMethod::Div => "__div",
LuaMetaMethod::Mod => "__mod",
LuaMetaMethod::Pow => "__pow",
LuaMetaMethod::Unm => "__unm",
LuaMetaMethod::IDiv => "__idiv",
LuaMetaMethod::BAnd => "__band",
LuaMetaMethod::BOr => "__bor",
LuaMetaMethod::BXor => "__bxor",
LuaMetaMethod::BNot => "__bnot",
LuaMetaMethod::Shl => "__shl",
LuaMetaMethod::Shr => "__shr",
2017-05-21 18:50:59 -05:00
LuaMetaMethod::Concat => "__concat",
LuaMetaMethod::Len => "__len",
LuaMetaMethod::Eq => "__eq",
LuaMetaMethod::Lt => "__lt",
LuaMetaMethod::Le => "__le",
LuaMetaMethod::Index => "__index",
LuaMetaMethod::NewIndex => "__newIndex",
LuaMetaMethod::Call => "__call",
LuaMetaMethod::ToString => "__tostring",
2017-05-21 18:50:59 -05:00
};
push_string(self.state, name);
2017-06-15 09:26:39 -05:00
self.push_value(
self.state,
LuaValue::Function(self.create_callback_function(m)?),
);
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
}
}
push_string(self.state, "__gc");
ffi::lua_pushcfunction(self.state, destructor::<RefCell<T>>);
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
push_string(self.state, "__metatable");
ffi::lua_pushboolean(self.state, 0);
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
let id = ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX);
entry.insert(id);
Ok(id)
}
}
})
}
}
static LUA_USERDATA_REGISTRY_KEY: u8 = 0;
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;