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
use core::ffi::CStr;
use crate::{AmountReturned, LuaState, ffi};
/// Userdata (full & light) related functions.
impl LuaState {
/// Returns `true` if the value at the given acceptable `index` is userdata (either full or light), `false` otherwise.
#[inline]
#[must_use]
pub fn is_userdata(&self, index: i32) -> bool {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_isuserdata(self.0, index) != 0 }
}
/// Returns `true` if the value at the given acceptable `index` is full userdata, `false` otherwise.
#[inline]
#[must_use]
pub fn is_full_userdata(&self, index: i32) -> bool {
self.is_userdata(index) && !self.is_light_userdata(index)
}
/// Returns `true` if the value at the given acceptable `index` is light userdata, `false` otherwise.
#[inline]
#[must_use]
pub fn is_light_userdata(&self, index: i32) -> bool {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_islightuserdata(self.0, index) != 0 }
}
/// Converts the value at the given acceptable `index` to full userdata and returns a mutable reference reference to its block.
///
/// # Safety
///
/// - The type of the element in the stack must be full userdata.
/// - `T` must be the same type used when creating this userdata.
/// - The userdata's value must be valid for `T`.
/// - There must not be another living reference to this userdata.
#[inline]
#[must_use]
#[allow(clippy::mut_from_ref)]
pub unsafe fn to_full_userdata<T>(&self, index: i32) -> &mut T {
// SAFETY: self.0 is always valid and the caller must uphold the safety conditions.
unsafe { &mut *ffi::lua_touserdata(self.0, index).cast() }
}
/// Converts the value at the given acceptable `index` to light userdata and returns it.
///
/// Only light and full userdata are accepted, anything else will return a null pointer.
#[inline]
#[must_use]
pub fn to_light_userdata<T>(&self, index: i32) -> *mut T {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_touserdata(self.0, index).cast() }
}
/// Allocates a new block the size of `T` containing `value`, pushes onto the stack a new full userdata with the block's address,
/// and then returns a mutable reference to said block.
///
/// Like any other Lua value, this userdata is inevitably collected by the garbage collector to be freed;
/// but by default this is done by simply deallocating the block instead of properly dropping the value!
///
/// *(This won't result in undefined behavior,
/// but could cause a memory leak for types that allocate dynamically or access some sort of external resource;
/// more primitive types should not need to be properly dropped.)*
///
/// This can be changed by having the `__gc` metamethod (which is triggered before the block is freed) drop the userdata.
/// For a simple way to do this, see [`LuaState::set_drop_gc_metamethod`].
#[inline]
pub fn new_full_userdata<T>(&mut self, value: T) -> &mut T {
// SAFETY: self.0 is always valid and lua_newuserdata should always return a pointer that is valid, aligned and not accessed anywhere else.
unsafe {
let ptr = ffi::lua_newuserdata(self.0, size_of::<T>()).cast::<T>();
ptr.write(value);
&mut *ptr
}
}
/// Behaves exactly as [`LuaState::new_full_userdata`], but uses `T`'s default value as the block's value.
#[inline]
pub fn new_full_userdata_default<T: Default>(&mut self) -> &mut T {
self.new_full_userdata(T::default())
}
/// Pushes the given `pointer` onto the stack as *light* userdata.
///
/// Unlike normal *full* userdata, light userdata is the raw pointer itself.
#[inline]
pub fn push_light_userdata<T>(&mut self, pointer: *mut T) {
// SAFETY: self.0 is always valid and pointer is simply stored, so it doesn't have to be valid.
unsafe { ffi::lua_pushlightuserdata(self.0, pointer.cast()); }
}
/// Checks if there's an userdata (either full or light) of type `userdata_type` at the given index `arg_n` and returns it as full userdata.
///
/// Userdata is of type `userdata_type` when it uses the metatable stored in the registry with key `userdata_type`
/// (see [`LuaState::new_userdata_metatable`]).
///
/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
///
/// # Safety
///
/// - `T` must be the same type used when creating this userdata.
/// - The userdata's value must be valid for `T`.
/// - There must not be another living reference to this userdata.
///
/// # Lua Errors
///
/// Intentionally errors if the check fails.
#[inline]
#[must_use]
#[allow(clippy::mut_from_ref)]
pub unsafe fn check_full_userdata<T>(&self, n_arg: i32, userdata_type: &CStr) -> &mut T {
unsafe { &mut *ffi::luaL_checkudata(self.0, n_arg, userdata_type.as_ptr()).cast() }
}
/// Checks if there's an userdata (either full or light) of type `userdata_type` at the given index `arg_n` and returns it as light userdata.
///
/// Userdata is of type `userdata_type` when it uses the metatable stored in the registry with key `userdata_type`
/// (see [`LuaState::new_userdata_metatable`]).
///
/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
///
/// # Lua Errors
///
/// Intentionally errors if the check fails.
#[inline]
#[must_use]
pub fn check_light_userdata<T>(&self, n_arg: i32, userdata_type: &CStr) -> *mut T {
unsafe { ffi::luaL_checkudata(self.0, n_arg, userdata_type.as_ptr()).cast() }
}
/// Finishes the currently called [`CFunction`], returning the given `pointer` as light userdata to the caller.
///
/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
///
/// [`CFunction`]: ./type.CFunction.html
#[inline]
pub fn return_light_userdata<T>(mut self, pointer: *mut T) -> AmountReturned {
self.push_light_userdata(pointer);
// SAFETY: There should always be at least one element in the stack.
unsafe { AmountReturned::unchecked(1) }
}
/// Pushes onto the stack the value in the registry with key `userdata_type`, returning `false`.
///
/// If no such value exists yet, a table will be set as the value (before being pushed) and `true` will be returned instead.
///
/// This function is used to get the metatable to be set as the metatable of the desired userdata (see [`LuaState::set_metatable`]).
///
/// # Examples
///
/// ```
/// use low_luajit::{LuaState, AmountReturned};
///
/// extern "C-unwind" fn index_metamethod(lua: LuaState) -> AmountReturned {
/// // ...
/// # AmountReturned::NONE
/// }
///
/// extern "C-unwind" fn my_light_userdata_constructor(mut lua: LuaState) -> AmountReturned {
/// # let ptr = core::ptr::null_mut::<()>();
/// // ...
///
/// // Construct the light userdata.
/// lua.push_light_userdata(ptr);
/// if lua.new_userdata_metatable(c"my_light_userdata") {
/// // Initialize the metatable.
/// lua.push_cfunction(index_metamethod);
/// lua.set_field(-2, c"__index");
/// // ...
/// }
/// lua.set_metatable(-2);
///
/// lua.return_any(1)
/// }
/// ```
#[inline]
pub fn new_userdata_metatable(&mut self, userdata_type: &CStr) -> bool {
unsafe { ffi::luaL_newmetatable(self.0, userdata_type.as_ptr()) != 0 }
}
/// Sets the `__gc` metamethod of the metatable at the top of the stack to a special destructor metamethod.
///
/// This metamethod will drop the full userdata (that is using the metatable) before the garbage collector frees the userdata's block.
///
/// This function is usually only necessary when the userdata's type implements `Drop`,
/// see [`LuaState::new_full_userdata`] for more information.
///
/// # Safety
///
/// - The type of the value that uses the metatable must be full userdata.
/// - `T` must be the same type used when creating this userdata.
/// - The userdata's value must be valid for `T`.
///
/// These conditions should always be true when [`LuaState::new_full_userdata`] and [`LuaState::new_userdata_metatable`] are used properly.
///
/// # Lua Errors
///
/// Not every top element can be indexed: for example, if `t` is `nil` this function will error.
///
/// Indexing the top element may also trigger the `__newindex` metamethod which could error.
///
/// # Examples
///
/// ```
/// # use low_luajit::{LuaState, AmountReturned};
/// # extern "C-unwind" fn example(mut lua: LuaState) -> AmountReturned {
/// lua.new_full_userdata(vec![36.2, 123.456, 0.998]);
/// if lua.new_userdata_metatable(c"my_full_userdata") {
/// // SAFETY: The metatable is being used by a valid full userdata of the same type.
/// unsafe { lua.set_drop_gc_metamethod::<Vec<f64>>() }
/// }
/// lua.set_metatable(-2);
/// # AmountReturned::NONE }
/// ```
///
/// *(For a more complete example on how to implement metatables for userdata, see [`LuaState::new_userdata_metatable`].)*
#[inline]
pub unsafe fn set_drop_gc_metamethod<T>(&mut self) {
extern "C-unwind" fn drop_userdata<T>(lua: LuaState) -> AmountReturned {
// SAFETY: The caller must uphold the safety conditions.
unsafe { lua.to_light_userdata::<T>(-1).drop_in_place() };
AmountReturned::NONE
}
self.push_cfunction(drop_userdata::<T>);
self.set_field(-2, c"__gc");
}
}