1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
|
use std::cell::{Ref, RefCell, RefMut};
use crate::error::{Error, Result};
use crate::ffi;
use crate::lua::Lua;
use crate::types::LuaRef;
use crate::util::{assert_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
/// Kinds of metamethods that can be overridden.
///
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
///
/// [`UserData`]: trait.UserData.html
#[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,
/// The `__tostring` metamethod.
///
/// This is not an operator, but will be called by methods such as `tostring` and `print`.
ToString,
}
impl MetaMethod {
pub(crate) fn name(self) -> &'static [u8] {
match self {
MetaMethod::Add => b"__add",
MetaMethod::Sub => b"__sub",
MetaMethod::Mul => b"__mul",
MetaMethod::Div => b"__div",
MetaMethod::Mod => b"__mod",
MetaMethod::Pow => b"__pow",
MetaMethod::Unm => b"__unm",
MetaMethod::IDiv => b"__idiv",
MetaMethod::BAnd => b"__band",
MetaMethod::BOr => b"__bor",
MetaMethod::BXor => b"__bxor",
MetaMethod::BNot => b"__bnot",
MetaMethod::Shl => b"__shl",
MetaMethod::Shr => b"__shr",
MetaMethod::Concat => b"__concat",
MetaMethod::Len => b"__len",
MetaMethod::Eq => b"__eq",
MetaMethod::Lt => b"__lt",
MetaMethod::Le => b"__le",
MetaMethod::Index => b"__index",
MetaMethod::NewIndex => b"__newindex",
MetaMethod::Call => b"__call",
MetaMethod::ToString => b"__tostring",
}
}
}
/// Method registry for [`UserData`] implementors.
///
/// [`UserData`]: trait.UserData.html
pub trait UserDataMethods<'lua, T: UserData> {
/// 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 set the `__index` metamethod, the `__index` metamethod will
/// be used as a fall-back if no regular method is found.
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
/// 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
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
/// Add a regular method as a function which accepts generic arguments, the first argument will
/// be a `UserData` of type T if the method is called with Lua method syntax:
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
///
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
///
/// [`add_method`]: #method.add_method
/// [`add_method_mut`]: #method.add_method_mut
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
/// Add a regular method as a mutable function which accepts generic arguments.
///
/// This is a version of [`add_function`] that accepts a FnMut argument.
///
/// [`add_function`]: #method.add_function
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
where
S: ?Sized + AsRef<[u8]>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
/// 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
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
/// 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
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
/// 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`.
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
/// Add a metamethod as a mutable function which accepts generic arguments.
///
/// This is a version of [`add_meta_function`] that accepts a FnMut argument.
///
/// [`add_meta_function`]: #method.add_meta_function
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F)
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
}
/// 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
///
/// ```
/// # use rlua::{Lua, UserData, Result};
/// # fn 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(())
/// # }
/// ```
///
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
/// [`UserDataMethods`] for more information):
///
/// ```
/// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result};
/// # fn main() -> Result<()> {
/// struct MyUserData(i32);
///
/// impl UserData for MyUserData {
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
/// 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(())
/// # }
/// ```
///
/// [`ToLua`]: trait.ToLua.html
/// [`FromLua`]: trait.FromLua.html
/// [`UserDataMethods`]: trait.UserDataMethods.html
pub trait UserData: Sized {
/// Adds custom methods and operators specific to this userdata.
fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(_methods: &mut T) {}
}
/// 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: 'static + UserData>(&self) -> bool {
match self.inspect(|_: &RefCell<T>| Ok(())) {
Ok(()) => true,
Err(Error::UserDataTypeMismatch) => false,
Err(_) => unreachable!(),
}
}
/// 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: 'static + UserData>(&self) -> Result<Ref<T>> {
self.inspect(|cell| Ok(cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?))
}
/// 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: 'static + UserData>(&self) -> Result<RefMut<T>> {
self.inspect(|cell| {
Ok(cell
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?)
})
}
/// Sets an associated value to this `AnyUserData`.
///
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`].
///
/// [`get_user_value`]: #method.get_user_value
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
let lua = self.0.lua;
let v = v.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(&self.0);
lua.push_value(v)?;
ffi::lua_setuservalue(lua.state, -2);
Ok(())
}
}
/// Returns an associated value set by [`set_user_value`].
///
/// [`set_user_value`]: #method.set_user_value
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
let lua = self.0.lua;
let res = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
lua.push_ref(&self.0);
ffi::lua_getuservalue(lua.state, -1);
lua.pop_value()
};
V::from_lua(res, lua)
}
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
where
T: 'static + UserData,
F: FnOnce(&'a RefCell<T>) -> Result<R>,
{
unsafe {
let lua = self.0.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 {
Err(Error::UserDataTypeMismatch)
} else {
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 {
Err(Error::UserDataTypeMismatch)
} else {
func(&*get_userdata::<RefCell<T>>(lua.state, -3))
}
}
}
}
}
|