crudely move LightUserData/UserData/AnyUserData to their own module
This commit is contained in:
parent
cf764fbabe
commit
7a45490aca
|
@ -6,6 +6,7 @@ use error::*;
|
||||||
use lua::*;
|
use lua::*;
|
||||||
use string::String;
|
use string::String;
|
||||||
use table::Table;
|
use table::Table;
|
||||||
|
use userdata::{LightUserData, UserData, AnyUserData};
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Value<'lua> {
|
impl<'lua> ToLua<'lua> for Value<'lua> {
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
|
|
@ -51,16 +51,17 @@ mod conversion;
|
||||||
mod multi;
|
mod multi;
|
||||||
mod string;
|
mod string;
|
||||||
mod table;
|
mod table;
|
||||||
|
mod userdata;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use error::{Error, Result, ExternalError, ExternalResult};
|
pub use error::{Error, Result, ExternalError, ExternalResult};
|
||||||
pub use lua::{Value, Nil, ToLua, FromLua, MultiValue, ToLuaMulti, FromLuaMulti, Integer, Number,
|
pub use lua::{Value, Nil, ToLua, FromLua, MultiValue, ToLuaMulti, FromLuaMulti, Integer, Number,
|
||||||
LightUserData, Function, ThreadStatus, Thread,
|
Function, ThreadStatus, Thread, Lua};
|
||||||
MetaMethod, UserDataMethods, UserData, AnyUserData, Lua};
|
|
||||||
pub use multi::Variadic;
|
pub use multi::Variadic;
|
||||||
pub use string::String;
|
pub use string::String;
|
||||||
pub use table::{Table, TablePairs, TableSequence};
|
pub use table::{Table, TablePairs, TableSequence};
|
||||||
|
pub use userdata::{LightUserData, MetaMethod, UserDataMethods, UserData, AnyUserData};
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
400
src/lua.rs
400
src/lua.rs
|
@ -1,7 +1,7 @@
|
||||||
use std::{fmt, ptr, str};
|
use std::{fmt, ptr, str};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::cell::{RefCell, Ref, RefMut};
|
use std::cell::RefCell;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -9,7 +9,6 @@ use std::collections::{HashMap, VecDeque};
|
||||||
use std::collections::hash_map::Entry as HashMapEntry;
|
use std::collections::hash_map::Entry as HashMapEntry;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::string::String as StdString;
|
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
|
@ -18,6 +17,7 @@ use error::*;
|
||||||
use util::*;
|
use util::*;
|
||||||
use string::String;
|
use string::String;
|
||||||
use table::Table;
|
use table::Table;
|
||||||
|
use userdata::{LightUserData, UserDataMethods, MetaMethod, UserData, AnyUserData};
|
||||||
|
|
||||||
/// A dynamically typed Lua value.
|
/// A dynamically typed Lua value.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -145,7 +145,10 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
|
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Callback<'lua> = Box<FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'lua>;
|
pub(crate) type Callback<'lua> = Box<
|
||||||
|
FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>
|
||||||
|
+ 'lua,
|
||||||
|
>;
|
||||||
|
|
||||||
pub(crate) struct LuaRef<'lua> {
|
pub(crate) struct LuaRef<'lua> {
|
||||||
pub lua: &'lua Lua,
|
pub lua: &'lua Lua,
|
||||||
|
@ -180,10 +183,6 @@ pub type Integer = ffi::lua_Integer;
|
||||||
/// Type of Lua floating point numbers.
|
/// Type of Lua floating point numbers.
|
||||||
pub type Number = ffi::lua_Number;
|
pub type Number = ffi::lua_Number;
|
||||||
|
|
||||||
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub struct LightUserData(pub *mut c_void);
|
|
||||||
|
|
||||||
/// Handle to an internal Lua function.
|
/// Handle to an internal Lua function.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Function<'lua>(LuaRef<'lua>);
|
pub struct Function<'lua>(LuaRef<'lua>);
|
||||||
|
@ -468,391 +467,6 @@ impl<'lua> Thread<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kinds of metamethods that can be overridden.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum MetaMethod {
|
|
||||||
/// The `+` operator.
|
|
||||||
Add,
|
|
||||||
/// The `-` operator.
|
|
||||||
Sub,
|
|
||||||
/// The `*` operator.
|
|
||||||
Mul,
|
|
||||||
/// The `/` operator.
|
|
||||||
Div,
|
|
||||||
/// The `%` operator.
|
|
||||||
Mod,
|
|
||||||
/// The `^` operator.
|
|
||||||
Pow,
|
|
||||||
/// The unary minus (`-`) operator.
|
|
||||||
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,
|
|
||||||
/// The string concatenation operator `..`.
|
|
||||||
Concat,
|
|
||||||
/// The length operator `#`.
|
|
||||||
Len,
|
|
||||||
/// The `==` operator.
|
|
||||||
Eq,
|
|
||||||
/// The `<` operator.
|
|
||||||
Lt,
|
|
||||||
/// The `<=` operator.
|
|
||||||
Le,
|
|
||||||
/// Index access `obj[key]`.
|
|
||||||
Index,
|
|
||||||
/// Index write access `obj[key] = value`.
|
|
||||||
NewIndex,
|
|
||||||
/// The call "operator" `obj(arg1, args2, ...)`.
|
|
||||||
Call,
|
|
||||||
/// tostring(ud) will call this if it exists
|
|
||||||
ToString,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method registry for [`UserData`] implementors.
|
|
||||||
///
|
|
||||||
/// [`UserData`]: trait.UserData.html
|
|
||||||
pub struct UserDataMethods<'lua, T> {
|
|
||||||
methods: HashMap<StdString, Callback<'lua>>,
|
|
||||||
meta_methods: HashMap<MetaMethod, Callback<'lua>>,
|
|
||||||
_type: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
|
||||||
/// Add a method which accepts a `&T` as the first parameter.
|
|
||||||
///
|
|
||||||
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
|
||||||
/// accessed method. This allows them to be used with the expected `userdata:method()` syntax.
|
|
||||||
///
|
|
||||||
/// If `add_meta_method` is used to override the `__index` metamethod, this approach will fall
|
|
||||||
/// back to the user-provided metamethod if no regular method was found.
|
|
||||||
pub fn add_method<A, R, M>(&mut self, name: &str, method: M)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
self.methods.insert(
|
|
||||||
name.to_owned(),
|
|
||||||
Self::box_method(method),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a regular method which accepts a `&mut T` as the first parameter.
|
|
||||||
///
|
|
||||||
/// Refer to [`add_method`] for more information about the implementation.
|
|
||||||
///
|
|
||||||
/// [`add_method`]: #method.add_method
|
|
||||||
pub fn add_method_mut<A, R, M>(&mut self, name: &str, method: M)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
self.methods.insert(
|
|
||||||
name.to_owned(),
|
|
||||||
Self::box_method_mut(method),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
|
||||||
/// always be a `UserData` of type T.
|
|
||||||
///
|
|
||||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
|
||||||
///
|
|
||||||
/// [`add_method`]: #method.add_method
|
|
||||||
/// [`add_method_mut`]: #method.add_method_mut
|
|
||||||
pub fn add_function<A, R, F>(&mut self, name: &str, function: F)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
self.methods.insert(
|
|
||||||
name.to_owned(),
|
|
||||||
Self::box_function(function),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a metamethod which accepts a `&T` as the first parameter.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
|
||||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
|
||||||
///
|
|
||||||
/// [`add_meta_function`]: #method.add_meta_function
|
|
||||||
pub fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
self.meta_methods.insert(meta, Self::box_method(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
|
||||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
|
||||||
///
|
|
||||||
/// [`add_meta_function`]: #method.add_meta_function
|
|
||||||
pub fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
self.meta_methods.insert(meta, Self::box_method_mut(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a metamethod which accepts generic arguments.
|
|
||||||
///
|
|
||||||
/// Metamethods 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`.
|
|
||||||
pub fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
self.meta_methods.insert(meta, Self::box_function(function));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn box_function<A, R, F>(mut function: F) -> Callback<'lua>
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
Box::new(move |lua, args| {
|
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(
|
|
||||||
lua,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn box_method<A, R, M>(mut method: M) -> Callback<'lua>
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
|
|
||||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
|
||||||
let userdata = userdata.borrow::<T>()?;
|
|
||||||
method(lua, &userdata, A::from_lua_multi(args, lua)?)?
|
|
||||||
.to_lua_multi(lua)
|
|
||||||
} else {
|
|
||||||
Err(Error::FromLuaConversionError {
|
|
||||||
from: "missing argument",
|
|
||||||
to: "userdata",
|
|
||||||
message: None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn box_method_mut<A, R, M>(mut method: M) -> Callback<'lua>
|
|
||||||
where
|
|
||||||
A: FromLuaMulti<'lua>,
|
|
||||||
R: ToLuaMulti<'lua>,
|
|
||||||
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
|
||||||
{
|
|
||||||
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
|
|
||||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
|
||||||
let mut userdata = userdata.borrow_mut::<T>()?;
|
|
||||||
method(lua, &mut userdata, A::from_lua_multi(args, lua)?)?
|
|
||||||
.to_lua_multi(lua)
|
|
||||||
} else {
|
|
||||||
Err(Error::FromLuaConversionError {
|
|
||||||
from: "missing argument",
|
|
||||||
to: "userdata",
|
|
||||||
message: None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for custom userdata types.
|
|
||||||
///
|
|
||||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
|
||||||
/// of [`ToLua`] and [`FromLua`] are automatically provided.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, UserData, Result};
|
|
||||||
/// # fn try_main() -> Result<()> {
|
|
||||||
/// struct MyUserData(i32);
|
|
||||||
///
|
|
||||||
/// impl UserData for MyUserData {}
|
|
||||||
///
|
|
||||||
/// let lua = Lua::new();
|
|
||||||
///
|
|
||||||
/// // `MyUserData` now implements `ToLua`:
|
|
||||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
|
||||||
///
|
|
||||||
/// lua.exec::<()>("assert(type(myobject) == 'userdata')", None)?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
|
||||||
/// [`UserDataMethods`] for more information):
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result};
|
|
||||||
/// # fn try_main() -> Result<()> {
|
|
||||||
/// struct MyUserData(i32);
|
|
||||||
///
|
|
||||||
/// impl UserData for MyUserData {
|
|
||||||
/// fn add_methods(methods: &mut UserDataMethods<Self>) {
|
|
||||||
/// methods.add_method("get", |_, this, _: ()| {
|
|
||||||
/// Ok(this.0)
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// methods.add_method_mut("add", |_, this, value: i32| {
|
|
||||||
/// this.0 += value;
|
|
||||||
/// Ok(())
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// methods.add_meta_method(MetaMethod::Add, |_, this, value: i32| {
|
|
||||||
/// Ok(this.0 + value)
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let lua = Lua::new();
|
|
||||||
///
|
|
||||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
|
||||||
///
|
|
||||||
/// lua.exec::<()>(r#"
|
|
||||||
/// assert(myobject:get() == 123)
|
|
||||||
/// myobject:add(7)
|
|
||||||
/// assert(myobject:get() == 130)
|
|
||||||
/// assert(myobject + 10 == 140)
|
|
||||||
/// "#, None)?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`ToLua`]: trait.ToLua.html
|
|
||||||
/// [`FromLua`]: trait.FromLua.html
|
|
||||||
/// [`UserDataMethods`]: struct.UserDataMethods.html
|
|
||||||
pub trait UserData: 'static + Sized {
|
|
||||||
/// Adds custom methods and operators specific to this userdata.
|
|
||||||
fn add_methods(_methods: &mut UserDataMethods<Self>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
|
||||||
///
|
|
||||||
/// Similar to `std::any::Any`, this provides an interface for dynamic type checking via the [`is`]
|
|
||||||
/// and [`borrow`] methods.
|
|
||||||
///
|
|
||||||
/// Internally, instances are stored in a `RefCell`, to best match the mutable semantics of the Lua
|
|
||||||
/// language.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
|
|
||||||
/// methods which check the type and acquire a borrow behind the scenes.
|
|
||||||
///
|
|
||||||
/// [`UserData`]: trait.UserData.html
|
|
||||||
/// [`is`]: #method.is
|
|
||||||
/// [`borrow`]: #method.borrow
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct AnyUserData<'lua>(LuaRef<'lua>);
|
|
||||||
|
|
||||||
impl<'lua> AnyUserData<'lua> {
|
|
||||||
/// Checks whether the type of this userdata is `T`.
|
|
||||||
pub fn is<T: UserData>(&self) -> bool {
|
|
||||||
self.inspect(|_: &RefCell<T>| ()).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrow this userdata immutably if it is of type `T`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
|
||||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
|
||||||
pub fn borrow<T: UserData>(&self) -> Result<Ref<T>> {
|
|
||||||
self.inspect(|cell| {
|
|
||||||
Ok(cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?)
|
|
||||||
}).ok_or(Error::UserDataTypeMismatch)?
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrow this userdata mutably if it is of type `T`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a
|
|
||||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
|
||||||
pub fn borrow_mut<T: UserData>(&self) -> Result<RefMut<T>> {
|
|
||||||
self.inspect(|cell| {
|
|
||||||
Ok(cell.try_borrow_mut().map_err(
|
|
||||||
|_| Error::UserDataBorrowMutError,
|
|
||||||
)?)
|
|
||||||
}).ok_or(Error::UserDataTypeMismatch)?
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inspect<'a, T, R, F>(&'a self, func: F) -> Option<R>
|
|
||||||
where
|
|
||||||
T: UserData,
|
|
||||||
F: FnOnce(&'a RefCell<T>) -> R,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let lua = self.0.lua;
|
|
||||||
stack_guard(lua.state, 0, move || {
|
|
||||||
check_stack(lua.state, 3);
|
|
||||||
|
|
||||||
lua.push_ref(lua.state, &self.0);
|
|
||||||
|
|
||||||
lua_assert!(
|
|
||||||
lua.state,
|
|
||||||
ffi::lua_getmetatable(lua.state, -1) != 0,
|
|
||||||
"AnyUserData missing metatable"
|
|
||||||
);
|
|
||||||
|
|
||||||
ffi::lua_rawgeti(
|
|
||||||
lua.state,
|
|
||||||
ffi::LUA_REGISTRYINDEX,
|
|
||||||
lua.userdata_metatable::<T>() as ffi::lua_Integer,
|
|
||||||
);
|
|
||||||
|
|
||||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
|
||||||
ffi::lua_pop(lua.state, 3);
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let res = func(&*get_userdata::<RefCell<T>>(lua.state, -3));
|
|
||||||
ffi::lua_pop(lua.state, 3);
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Top level Lua struct which holds the Lua state itself.
|
/// Top level Lua struct which holds the Lua state itself.
|
||||||
pub struct Lua {
|
pub struct Lua {
|
||||||
pub(crate) state: *mut ffi::lua_State,
|
pub(crate) state: *mut ffi::lua_State,
|
||||||
|
@ -1499,7 +1113,7 @@ impl Lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn userdata_metatable<T: UserData>(&self) -> c_int {
|
pub(crate) unsafe fn userdata_metatable<T: UserData>(&self) -> c_int {
|
||||||
// Used if both an __index metamethod is set and regular methods, checks methods table
|
// Used if both an __index metamethod is set and regular methods, checks methods table
|
||||||
// first, then __index metamethod.
|
// first, then __index metamethod.
|
||||||
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
|
|
@ -0,0 +1,399 @@
|
||||||
|
use std::cell::{RefCell, Ref, RefMut};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::string::String as StdString;
|
||||||
|
|
||||||
|
use ffi;
|
||||||
|
use error::*;
|
||||||
|
use util::*;
|
||||||
|
use lua::{FromLua, FromLuaMulti, ToLuaMulti, Callback, LuaRef, Lua};
|
||||||
|
|
||||||
|
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct LightUserData(pub *mut c_void);
|
||||||
|
|
||||||
|
/// Kinds of metamethods that can be overridden.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum MetaMethod {
|
||||||
|
/// The `+` operator.
|
||||||
|
Add,
|
||||||
|
/// The `-` operator.
|
||||||
|
Sub,
|
||||||
|
/// The `*` operator.
|
||||||
|
Mul,
|
||||||
|
/// The `/` operator.
|
||||||
|
Div,
|
||||||
|
/// The `%` operator.
|
||||||
|
Mod,
|
||||||
|
/// The `^` operator.
|
||||||
|
Pow,
|
||||||
|
/// The unary minus (`-`) operator.
|
||||||
|
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,
|
||||||
|
/// The string concatenation operator `..`.
|
||||||
|
Concat,
|
||||||
|
/// The length operator `#`.
|
||||||
|
Len,
|
||||||
|
/// The `==` operator.
|
||||||
|
Eq,
|
||||||
|
/// The `<` operator.
|
||||||
|
Lt,
|
||||||
|
/// The `<=` operator.
|
||||||
|
Le,
|
||||||
|
/// Index access `obj[key]`.
|
||||||
|
Index,
|
||||||
|
/// Index write access `obj[key] = value`.
|
||||||
|
NewIndex,
|
||||||
|
/// The call "operator" `obj(arg1, args2, ...)`.
|
||||||
|
Call,
|
||||||
|
/// tostring(ud) will call this if it exists
|
||||||
|
ToString,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method registry for [`UserData`] implementors.
|
||||||
|
///
|
||||||
|
/// [`UserData`]: trait.UserData.html
|
||||||
|
pub struct UserDataMethods<'lua, T> {
|
||||||
|
pub(crate) methods: HashMap<StdString, Callback<'lua>>,
|
||||||
|
pub(crate) meta_methods: HashMap<MetaMethod, Callback<'lua>>,
|
||||||
|
pub(crate) _type: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
||||||
|
/// Add a method which accepts a `&T` as the first parameter.
|
||||||
|
///
|
||||||
|
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
||||||
|
/// accessed method. This allows them to be used with the expected `userdata:method()` syntax.
|
||||||
|
///
|
||||||
|
/// If `add_meta_method` is used to override the `__index` metamethod, this approach will fall
|
||||||
|
/// back to the user-provided metamethod if no regular method was found.
|
||||||
|
pub fn add_method<A, R, M>(&mut self, name: &str, method: M)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.insert(
|
||||||
|
name.to_owned(),
|
||||||
|
Self::box_method(method),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a regular method which accepts a `&mut T` as the first parameter.
|
||||||
|
///
|
||||||
|
/// Refer to [`add_method`] for more information about the implementation.
|
||||||
|
///
|
||||||
|
/// [`add_method`]: #method.add_method
|
||||||
|
pub fn add_method_mut<A, R, M>(&mut self, name: &str, method: M)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.insert(
|
||||||
|
name.to_owned(),
|
||||||
|
Self::box_method_mut(method),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||||
|
/// always be a `UserData` of type T.
|
||||||
|
///
|
||||||
|
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
||||||
|
///
|
||||||
|
/// [`add_method`]: #method.add_method
|
||||||
|
/// [`add_method_mut`]: #method.add_method_mut
|
||||||
|
pub fn add_function<A, R, F>(&mut self, name: &str, function: F)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.insert(
|
||||||
|
name.to_owned(),
|
||||||
|
Self::box_function(function),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a metamethod which accepts a `&T` as the first parameter.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
||||||
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||||
|
///
|
||||||
|
/// [`add_meta_function`]: #method.add_meta_function
|
||||||
|
pub fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.insert(meta, Self::box_method(method));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
||||||
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||||
|
///
|
||||||
|
/// [`add_meta_function`]: #method.add_meta_function
|
||||||
|
pub fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.insert(meta, Self::box_method_mut(method));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a metamethod which accepts generic arguments.
|
||||||
|
///
|
||||||
|
/// Metamethods 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`.
|
||||||
|
pub fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.insert(meta, Self::box_function(function));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_function<A, R, F>(mut function: F) -> Callback<'lua>
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
Box::new(move |lua, args| {
|
||||||
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(
|
||||||
|
lua,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_method<A, R, M>(mut method: M) -> Callback<'lua>
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
|
||||||
|
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||||
|
let userdata = userdata.borrow::<T>()?;
|
||||||
|
method(lua, &userdata, A::from_lua_multi(args, lua)?)?
|
||||||
|
.to_lua_multi(lua)
|
||||||
|
} else {
|
||||||
|
Err(Error::FromLuaConversionError {
|
||||||
|
from: "missing argument",
|
||||||
|
to: "userdata",
|
||||||
|
message: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_method_mut<A, R, M>(mut method: M) -> Callback<'lua>
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
|
||||||
|
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||||
|
let mut userdata = userdata.borrow_mut::<T>()?;
|
||||||
|
method(lua, &mut userdata, A::from_lua_multi(args, lua)?)?
|
||||||
|
.to_lua_multi(lua)
|
||||||
|
} else {
|
||||||
|
Err(Error::FromLuaConversionError {
|
||||||
|
from: "missing argument",
|
||||||
|
to: "userdata",
|
||||||
|
message: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for custom userdata types.
|
||||||
|
///
|
||||||
|
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
||||||
|
/// of [`ToLua`] and [`FromLua`] are automatically provided.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate rlua;
|
||||||
|
/// # use rlua::{Lua, UserData, Result};
|
||||||
|
/// # fn try_main() -> Result<()> {
|
||||||
|
/// struct MyUserData(i32);
|
||||||
|
///
|
||||||
|
/// impl UserData for MyUserData {}
|
||||||
|
///
|
||||||
|
/// let lua = Lua::new();
|
||||||
|
///
|
||||||
|
/// // `MyUserData` now implements `ToLua`:
|
||||||
|
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||||
|
///
|
||||||
|
/// lua.exec::<()>("assert(type(myobject) == 'userdata')", None)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// # fn main() {
|
||||||
|
/// # try_main().unwrap();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
||||||
|
/// [`UserDataMethods`] for more information):
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate rlua;
|
||||||
|
/// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result};
|
||||||
|
/// # fn try_main() -> Result<()> {
|
||||||
|
/// struct MyUserData(i32);
|
||||||
|
///
|
||||||
|
/// impl UserData for MyUserData {
|
||||||
|
/// fn add_methods(methods: &mut UserDataMethods<Self>) {
|
||||||
|
/// methods.add_method("get", |_, this, _: ()| {
|
||||||
|
/// Ok(this.0)
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// methods.add_method_mut("add", |_, this, value: i32| {
|
||||||
|
/// this.0 += value;
|
||||||
|
/// Ok(())
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// methods.add_meta_method(MetaMethod::Add, |_, this, value: i32| {
|
||||||
|
/// Ok(this.0 + value)
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let lua = Lua::new();
|
||||||
|
///
|
||||||
|
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||||
|
///
|
||||||
|
/// lua.exec::<()>(r#"
|
||||||
|
/// assert(myobject:get() == 123)
|
||||||
|
/// myobject:add(7)
|
||||||
|
/// assert(myobject:get() == 130)
|
||||||
|
/// assert(myobject + 10 == 140)
|
||||||
|
/// "#, None)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// # fn main() {
|
||||||
|
/// # try_main().unwrap();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`ToLua`]: trait.ToLua.html
|
||||||
|
/// [`FromLua`]: trait.FromLua.html
|
||||||
|
/// [`UserDataMethods`]: struct.UserDataMethods.html
|
||||||
|
pub trait UserData: 'static + Sized {
|
||||||
|
/// Adds custom methods and operators specific to this userdata.
|
||||||
|
fn add_methods(_methods: &mut UserDataMethods<Self>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
||||||
|
///
|
||||||
|
/// Similar to `std::any::Any`, this provides an interface for dynamic type checking via the [`is`]
|
||||||
|
/// and [`borrow`] methods.
|
||||||
|
///
|
||||||
|
/// Internally, instances are stored in a `RefCell`, to best match the mutable semantics of the Lua
|
||||||
|
/// language.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
|
||||||
|
/// methods which check the type and acquire a borrow behind the scenes.
|
||||||
|
///
|
||||||
|
/// [`UserData`]: trait.UserData.html
|
||||||
|
/// [`is`]: #method.is
|
||||||
|
/// [`borrow`]: #method.borrow
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
|
|
||||||
|
impl<'lua> AnyUserData<'lua> {
|
||||||
|
/// Checks whether the type of this userdata is `T`.
|
||||||
|
pub fn is<T: UserData>(&self) -> bool {
|
||||||
|
self.inspect(|_: &RefCell<T>| ()).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrow this userdata immutably if it is of type `T`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||||
|
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||||
|
pub fn borrow<T: UserData>(&self) -> Result<Ref<T>> {
|
||||||
|
self.inspect(|cell| {
|
||||||
|
Ok(cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?)
|
||||||
|
}).ok_or(Error::UserDataTypeMismatch)?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrow this userdata mutably if it is of type `T`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a
|
||||||
|
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||||
|
pub fn borrow_mut<T: UserData>(&self) -> Result<RefMut<T>> {
|
||||||
|
self.inspect(|cell| {
|
||||||
|
Ok(cell.try_borrow_mut().map_err(
|
||||||
|
|_| Error::UserDataBorrowMutError,
|
||||||
|
)?)
|
||||||
|
}).ok_or(Error::UserDataTypeMismatch)?
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inspect<'a, T, R, F>(&'a self, func: F) -> Option<R>
|
||||||
|
where
|
||||||
|
T: UserData,
|
||||||
|
F: FnOnce(&'a RefCell<T>) -> R,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let lua = self.0.lua;
|
||||||
|
stack_guard(lua.state, 0, move || {
|
||||||
|
check_stack(lua.state, 3);
|
||||||
|
|
||||||
|
lua.push_ref(lua.state, &self.0);
|
||||||
|
|
||||||
|
lua_assert!(
|
||||||
|
lua.state,
|
||||||
|
ffi::lua_getmetatable(lua.state, -1) != 0,
|
||||||
|
"AnyUserData missing metatable"
|
||||||
|
);
|
||||||
|
|
||||||
|
ffi::lua_rawgeti(
|
||||||
|
lua.state,
|
||||||
|
ffi::LUA_REGISTRYINDEX,
|
||||||
|
lua.userdata_metatable::<T>() as ffi::lua_Integer,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||||
|
ffi::lua_pop(lua.state, 3);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let res = func(&*get_userdata::<RefCell<T>>(lua.state, -3));
|
||||||
|
ffi::lua_pop(lua.state, 3);
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue