low_luajit/lua/type/function.rs
1use core::{ffi::{CStr, c_int, c_void}, mem, num::NonZeroI32, slice};
2use crate::{LuaState, SafeFn, ffi::{self, lua_CFunction, lua_State}};
3
4/// A safe function using the C ABI that Lua can call.
5///
6/// This type is technically a *subset* of [`lua_CFunction`], as it only allows safe functions.
7/// Different methods will use either [`lua_CFunction`] or `CFunction` based on if they work with only safe functions or not.
8///
9/// It is always safe to convert a `CFunction` into a [`lua_CFunction`] (see [`SafeFn::to_raw`]).
10///
11/// # Protocol
12///
13/// To communicate properly, Lua expects `CFunction`s to follow a specific protocol:
14///
15/// Arguments passed to the `CFunction` are located in the stack (index 1 contains the first argument, etc.),
16/// thus [`LuaState::get_top`] can be used to know the number of arguments received.
17///
18/// Values to return to the caller should be pushed onto the stack (first value is returned first, etc.),
19/// using the returned [`AmountReturned`] to specify how many values were returned.
20///
21/// # Examples
22///
23/// ```
24/// # use low_luajit::{LuaState, AmountReturned};
25/// extern "C-unwind" fn sum(mut lua: LuaState) -> AmountReturned {
26/// // Retrieve the arguments, asserting that they exist and are of the correct type.
27/// let x = lua.check_number(1);
28/// let y = lua.check_number(2);
29///
30/// // Push the value to return and return 1 since we're only returning a single value.
31/// lua.push_number(x + y);
32/// return lua.finish(1);
33/// }
34/// ```
35///
36/// [`lua_CFunction`]: ./ffi/type.lua_CFunction.html
37pub type CFunction = extern "C-unwind" fn(lua: LuaState) -> AmountReturned;
38
39impl SafeFn for CFunction {
40 type RawFn = lua_CFunction;
41
42 #[inline]
43 unsafe fn from_raw(raw: lua_CFunction) -> Self {
44 // SAFETY: lua_CFunction and CFunction are effectively the same.
45 unsafe { mem::transmute(raw) }
46 }
47
48 #[inline]
49 fn to_raw(self) -> lua_CFunction {
50 // SAFETY: CFunction and lua_CFunction are effectively the same.
51 unsafe { mem::transmute(self) }
52 }
53}
54
55/// Simple wrapper that tells Lua how many values a [`CFunction`] is intending to return.
56///
57/// Without this wrapper, Lua would willingly attempt to return as many values as you say,
58/// which can easily go beyond the stack and result in undefined behavior.
59///
60/// This wrapper can only be (safely) created by [`LuaState`],
61/// which will check if the amount is valid before returning the `AmountReturned` (see [`LuaState::finish`]).
62#[repr(transparent)]
63#[must_use = "this `AmountReturned` should be returned to the caller"]
64pub struct AmountReturned(core::ffi::c_int);
65
66impl AmountReturned {
67 /// Tells Lua that no values are intended to be returned.
68 ///
69 /// Always safe to use since nothing gets popped.
70 pub const NONE: Self = Self(0);
71
72 /// Wraps `amount` around an `AmountReturned`, without doing any checks.
73 ///
74 /// # Safety
75 ///
76 /// `amount` must not be higher than the amount of values the Lua stack is holding at the time of returning.
77 pub const unsafe fn unchecked(amount: i32) -> Self {
78 Self(amount)
79 }
80}
81
82/// Trait for all closures and functions that can be used with [`LuaState::dump`].
83///
84/// See [`LuaState::dump`] for more information.
85pub trait Writer: FnMut(&LuaState, &[u8]) -> Result<(), NonZeroI32> {}
86impl<T: FnMut(&LuaState, &[u8]) -> Result<(), NonZeroI32>> Writer for T {}
87
88/*/// Trait for all closures and functions that can be used with [`LuaState::load`].
89///
90/// See [`LuaState::load`] for more information.
91pub trait Reader<T: AsRef<[u8]>>: FnMut(&mut LuaState) -> Option<T> {}
92impl<T: AsRef<[u8]>, U: FnMut(&mut LuaState) -> Option<T>> Reader<T> for U {}*/
93
94/// All error codes that a function may return when erroring while called in protected mode.
95///
96/// This enum is used by the following functions:
97/// - [`LuaState::pcall`].
98/// - [`LuaState::cpcall`].
99#[repr(i32)]
100#[derive(Debug)]
101pub enum PCallError {
102 /// Any error during runtime.
103 RuntimeError = ffi::LUA_ERRRUN,
104
105 /// Memory allocation error (rare).
106 ///
107 /// *([`LuaState::pcall`] will not call the error handler for these errors.)*
108 MemoryError = ffi::LUA_ERRMEM,
109
110 /// Error while calling the error handler.
111 ErrorHandlerError = ffi::LUA_ERRERR,
112}
113
114impl PCallError {
115 /// Converts the given `value` into a `Result`.
116 ///
117 /// # Safety
118 ///
119 /// `value` must either be `0` or a valid `PCallError` value.
120 ///
121 /// # Errors
122 ///
123 /// Returns `Err` if `value` is not `0`.
124 #[inline]
125 pub const unsafe fn check_raw(value: i32) -> Result<(), Self> {
126 // SAFETY: The caller must only give 0 or a valid error code.
127 if value == 0 { Ok(()) } else { Err(unsafe { mem::transmute::<i32, PCallError>(value) }) }
128 }
129}
130
131/// All error codes that may be returned when failing to load a chunk.
132///
133/// This enum is used by the following functions:
134/// - [`LuaState::load_bytes`].
135/// - [`LuaState::load_cstring`].
136/// - [`LuaState::load_file`].
137#[repr(i32)]
138#[derive(Debug)]
139pub enum LoadError {
140 /// Syntax error during pre-compilation.
141 SyntaxError = ffi::LUA_ERRSYNTAX,
142
143 /// Memory allocation error (rare).
144 MemoryError = ffi::LUA_ERRMEM,
145
146 /// *(Only returned by [`LuaState::load_file`].)*
147 FileError = ffi::LUA_ERRFILE,
148}
149
150impl LoadError {
151 /// Converts the given `value` into a `Result`.
152 ///
153 /// # Safety
154 ///
155 /// `value` must either be `0` or a valid `LoadError` value.
156 ///
157 /// # Errors
158 ///
159 /// Returns `Err` if `value` is not `0`.
160 #[inline]
161 pub const unsafe fn check_raw(value: i32) -> Result<(), Self> {
162 // SAFETY: The caller must only give 0 or a valid error code.
163 if value == 0 { Ok(()) } else { Err(unsafe { mem::transmute::<i32, LoadError>(value) }) }
164 }
165}
166
167/// Function related functions.
168impl LuaState {
169 /// Finishes the currently called [`CFunction`] by returning the [`AmountReturned`] value to be returned to the calller.
170 ///
171 /// The `LuaState` wrapper itself is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
172 ///
173 /// # Panics
174 ///
175 /// Panics if `amount` is higher than the amount of values currently held in the stack ([`LuaState::get_top`]).
176 #[inline]
177 pub fn finish(self, amount: i32) -> AmountReturned {
178 assert!(amount >= 0, "cannot return a negative amount of values");
179 assert!(amount <= self.get_top(), "tried to return {amount} values but the stack currently holds {} values", self.get_top());
180
181 // SAFETY: amount should not be higher than the amount of values currently present in the stack.
182 unsafe { AmountReturned::unchecked(amount) }
183 }
184
185 /// Returns `true` if the value at the given acceptable `index` is a function (either [`CFunction`] or Lua function), `false` otherwise.
186 #[inline]
187 #[must_use]
188 pub fn is_function(&self, index: i32) -> bool {
189 // SAFETY: self.0 is always valid.
190 unsafe { ffi::lua_isfunction(self.0, index) != 0 }
191 }
192
193 /// Returns `true` if the value at the given acceptable `index` is a [`CFunction`], `false` otherwise.
194 #[inline]
195 #[must_use]
196 pub fn is_cfunction(&self, index: i32) -> bool {
197 // SAFETY: self.0 is always valid.
198 unsafe { ffi::lua_iscfunction(self.0, index) != 0 }
199 }
200
201 /// Returns `true` if the value at the given acceptable `index` is a Lua function, `false` otherwise.
202 #[inline]
203 #[must_use]
204 pub fn is_lua_function(&self, index: i32) -> bool {
205 self.is_function(index) && !self.is_cfunction(index)
206 }
207
208 /// Returns the [`lua_CFunction`] at the given `index`, or `None` if the value is anything else.
209 ///
210 /// [`lua_CFunction`] is returned instead of [`CFunction`] as this function cannot determine its safety.
211 /// Additionally, this function may sometimes return `None` for some C functions even if [`LuaState::is_cfunction`] returns `true`.
212 ///
213 /// If you just want to call a function, you probably want to use [`LuaState::call`], [`LuaState::pcall`], or [`LuaState::cpcall`] instead.
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// # use low_luajit::{LuaState, AmountReturned};
219 /// # extern "C-unwind" fn example(lua: LuaState) -> AmountReturned {
220 /// let f = lua.to_cfunction(-1).unwrap();
221 /// // SAFETY: This function is safe to call.
222 /// unsafe { f(lua.as_ptr()) };
223 /// # AmountReturned::NONE }
224 /// ```
225 #[inline]
226 #[must_use]
227 pub fn to_cfunction(&self, index: i32) -> Option<lua_CFunction> {
228 // SAFETY: self.0 is always valid.
229 unsafe { ffi::lua_tocfunction(self.0, index) }
230 }
231
232 /// Pushes the given [`CFunction`] `func` onto the stack.
233 ///
234 /// To associate values with `func`, see [`LuaState::push_cclosure`].
235 #[inline]
236 pub fn push_cfunction(&mut self, func: CFunction) {
237 // SAFETY: self.0 and transmuting from CFunction to lua_CFunction are always valid.
238 unsafe { ffi::lua_pushcfunction(self.0, func.to_raw()); }
239 }
240
241 /// Pushes the given [`CFunction`] `func` onto the stack, while also associating `n` values to it.
242 ///
243 /// To associate values with `func`, simply push said values before calling this function;
244 /// they will be popped but will be accessible from within `func` at the pseudo-indices returned by [`LuaState::upvalue_index`].
245 ///
246 /// If `n` is `0` this function will behave exactly as [`LuaState::push_cfunction`].
247 ///
248 /// # Panics
249 ///
250 /// Panics if the stack contains less than `n` values currently.
251 #[inline]
252 pub fn push_cclosure(&mut self, func: CFunction, n: u8) {
253 assert!(i32::from(n) <= self.get_top(), "tried to push a `CFunction` with {n} upvalues but the stack currently holds {} values", self.get_top());
254
255 // SAFETY: self.0 is always valid, the raw version of func is safe to call by Lua, and the stack contains enough values to pop.
256 unsafe { ffi::lua_pushcclosure(self.0, func.to_raw(), i32::from(n)); }
257 }
258
259 /// Sets the given function `func` as a global named `name`.
260 ///
261 /// # Lua Errors
262 ///
263 /// Indexing the global environment may trigger the `__newindex` metamethod which could error.
264 #[inline]
265 pub fn register_function(&mut self, name: &CStr, func: CFunction) {
266 // SAFETY: self.0 and name's pointer are always valid, and the raw version of func is safe to call by Lua.
267 unsafe { ffi::lua_register(self.0, name.as_ptr(), func.to_raw()); }
268 }
269
270 /// When passed as the number of expected results for the called functions in [`LuaState::call`] or [`LuaState::pcall`],
271 /// it will tell Lua to push to the stack exactly as many values as the called function returned.
272 pub const MULTI_RETURN: i32 = ffi::LUA_MULTRET;
273
274 /// Calls a function.
275 ///
276 /// To properly call a function, follow these steps:
277 /// 1. Push the function onto the stack.
278 /// 2. Push the arguments of the function (as many as `n_args`), pushing the first arg first and so on.
279 /// 3. Call this function, which will pop the function and arguments and push as the results (as many as `n_results`) in the same order.
280 ///
281 /// If `n_results` is [`LuaState::MULTI_RETURN`] (`-1`), all the results from the function are pushed to the stack.
282 /// If `n_results` is higher than the amount of values returned by the function, additional `nil`s will be pushed to compensate.
283 ///
284 /// # Panics
285 ///
286 /// Panics if the stack does not contain the function itself and its arguments.
287 ///
288 /// # Lua Errors
289 ///
290 /// The called function itself may error, to catch any error consider using [`LuaState::pcall`] or [`LuaState::cpcall`] instead.
291 ///
292 /// Additionally, this function will error if the first element popped (the function to call) is not a function.
293 #[inline]
294 pub fn call(&mut self, n_args: i32, n_results: i32) {
295 let top = self.get_top();
296 assert!(top > 0, "tried to call a function despite having an empty stack");
297 assert!(n_args < top, "cannot pass {n_args} arguments to the called function, as there are only {top} elements");
298
299 // SAFETY: self.0 is always valid and the stack contains enough values to pop.
300 unsafe { ffi::lua_call(self.0, n_args, n_results); }
301 }
302
303 /// Calls a function in protected mode.
304 ///
305 /// See [`LuaState::call`] for how to properly call a function.
306 ///
307 /// If the called function does not error, this function behaves exactly like [`LuaState::call`].
308 ///
309 /// `error_func` should either be `0` or the index to an error handler function,
310 /// which will be called on error and its returned value will be treated as the error object instead of the original error message.
311 /// *(Pesudo-indices cannot be used for `error_func`.)*
312 ///
313 /// # Panics
314 ///
315 /// Panics if the stack does not contain the function itself and its arguments.
316 ///
317 /// # Errors
318 ///
319 /// If the called function triggers a Lua error,
320 /// `Err` will be returned with the appropriate [`PCallError`] and the error object is pushed onto of the stack.
321 ///
322 /// Additionally, this function will error if the first element popped (the function to call)
323 /// or the element at index `error_func` are not functions.
324 #[inline]
325 pub fn pcall(&mut self, n_args: i32, n_results: i32, error_func: i32) -> Result<(), PCallError> {
326 let top = self.get_top();
327 assert!(top > 0, "tried to pcall a function despite having an empty stack");
328 assert!(n_args < top, "cannot pass {n_args} arguments to the pcalled function, as there are only {top} elements");
329
330 // SAFETY: self.0 is always valid, the stack contains enough values to pop, and the returned value is either 0 or a valid PCallError.
331 unsafe { PCallError::check_raw(ffi::lua_pcall(self.0, n_args, n_results, error_func)) }
332 }
333
334 /// Calls the function `func` in protected mode with a single argument: a light userdata containing `ud`.
335 ///
336 /// Any value returned by `func` will be discarded (the stack is not changed).
337 ///
338 /// # Errors
339 ///
340 /// If `func` triggers a Lua error,
341 /// `Err` will be returned with the appropriate [`PCallError`] and the error object is pushed onto of the stack.
342 #[inline]
343 pub fn cpcall<T>(&mut self, func: CFunction, ud: *mut T) -> Result<(), PCallError> {
344 // SAFETY: self.0 is always valid, func is safe to call, ud is only stored and the returned value is either 0 or a valid PCallError.
345 unsafe { PCallError::check_raw(ffi::lua_cpcall(self.0, func.to_raw(), ud.cast())) }
346 }
347
348 /// Dumps the Lua function at the top of the stack as a binary chunk ([`CFunction`]s cannot be dumped).
349 ///
350 /// Dumping is done by sending fragments of the chunk to the `writer` function, which should combine them together into the full chunk.
351 ///
352 /// If an error happens during dumping, `Err` is returned with any non-zero number.
353 ///
354 /// Reloading the chunk results in a function equivalent to the one originally dumped (see [`LuaState::load_bytes`]).
355 ///
356 /// # Errors
357 ///
358 /// If the top element is not a Lua function (or the stack is empty), this function will error returning `1`.
359 ///
360 /// `writer` may error and return any other non-zero number (`1` included), based on its implementation.
361 ///
362 /// # Examples
363 ///
364 /// ```
365 /// # use low_luajit::{LuaState, AmountReturned};
366 /// # extern "C-unwind" fn example(lua: LuaState) -> AmountReturned {
367 /// if lua.is_lua_function(-1) {
368 /// let mut chunk = Vec::new();
369 /// lua.dump(|_, fragment| {
370 /// chunk.extend_from_slice(fragment);
371 /// Ok(())
372 /// }).expect("this should never error");
373 /// // (Use the chunk in some way...)
374 /// }
375 /// # AmountReturned::NONE }
376 /// ```
377 #[inline]
378 pub fn dump<F: Writer>(&self, mut writer: F) -> Result<(), NonZeroI32> {
379 unsafe extern "C-unwind" fn middleware<F: Writer>(lua: *mut lua_State, ptr: *const c_void, size: usize, writer: *mut c_void) -> c_int {
380 // SAFETY: writer should be a valid pointer to the writer of the upper function, the lua_State should always be valid,
381 // ptr should be non-null and valid for size amount of values, and Result<(), NonZeroI32> can be transmuted to i32.
382 unsafe { mem::transmute((&mut *writer.cast::<F>())(&LuaState::from_ptr(lua), slice::from_raw_parts(ptr.cast(), size))) }
383 }
384
385 // SAFETY: self.0 is always valid, lua_dump calls middleware properly, and i32 can be transmuted to Result<(), NonZeroI32>.
386 unsafe { mem::transmute(ffi::lua_dump(self.0, middleware::<F>, (&raw mut writer).cast())) }
387 }
388
389 /// Loads the given `bytes` as a Lua chunk named `chunk_name`, pushing the resulting function onto the stack (if successful).
390 ///
391 /// *(The resulting function is only pushed, not called.)*
392 ///
393 /// While `chunk_name` can be any value, there are some common naming conventions:
394 /// - If the name is the path to a file, it should be prefixed with `'@'`. *([`LuaState::load_file`] does this.)*
395 /// - If the name is a custom identifier for the function, it should be prefixed with `'='`. *(All [`CFunction`]s are named `"=[C]"`.)*
396 /// - Anything else will be interpreted as if being the function's source itself. *([`LuaState::load_cstring`] does this.)*
397 ///
398 /// # Errors
399 ///
400 /// If unable to load the given `bytes`,
401 /// `Err` will be returned with the appropriate [`LoadError`] and the error message is pushed onto the stack.
402 #[inline]
403 pub fn load_bytes(&mut self, bytes: &[u8], chunk_name: &CStr) -> Result<(), LoadError> {
404 // SAFETY: self.0, bytes and chunk_name are always valid, u8 can be casted as c_char (they're both 1 byte),
405 // the length is correct as it's returned from the array itself, and the returned value is either 0 or a valid LoadError.
406 unsafe { LoadError::check_raw(ffi::luaL_loadbuffer(self.0, bytes.as_ptr().cast(), bytes.len(), chunk_name.as_ptr())) }
407 }
408
409 /// Loads the given `cstring` as a Lua chunk, pushing the resulting function onto the stack (if successful).
410 ///
411 /// *(The resulting function is only pushed, not called.)*
412 ///
413 /// The chunk will be named after the given `cstring` itself.
414 ///
415 /// # Errors
416 ///
417 /// If unable to load the given `cstring`,
418 /// `Err` will be returned with the appropriate [`LoadError`] and the error message is pushed onto the stack.
419 #[inline]
420 pub fn load_cstring(&mut self, cstring: &CStr) -> Result<(), LoadError> {
421 // SAFETY: self.0 and cstring's pointer are always valid and the returned value is either 0 or a valid LoadError.
422 unsafe { LoadError::check_raw(ffi::luaL_loadstring(self.0, cstring.as_ptr())) }
423 }
424
425 /// Attempts to open the file named `filename` and load it as a Lua chunk, pushing the resulting function onto the stack (if successful).
426 ///
427 /// *(The resulting function is only pushed, not called.)*
428 ///
429 /// The chunk will be named after the given `filename` itself.
430 ///
431 /// # Errors
432 ///
433 /// If unable to open the file or load its contents,
434 /// `Err` will be returned with the appropriate [`LoadError`] and the error message is pushed onto the stack.
435 #[inline]
436 pub fn load_file(&mut self, filename: &CStr) -> Result<(), LoadError> {
437 // SAFETY: self.0 and filename's pointer are always valid and the returned value is either 0 or a valid LoadError.
438 unsafe { LoadError::check_raw(ffi::luaL_loadfile(self.0, filename.as_ptr())) }
439 }
440
441 /*#[inline] TODO: this doesn't work, also it might be over the top? maybe it should just accept the chunk as a simple variable?
442 pub fn load<T: AsRef<[u8]>, F: Reader<T>>(&mut self, mut reader: F, chunk_name: &CStr) {
443 unsafe extern "C-unwind" fn middleware<T: AsRef<[u8]>, F: Reader<T>>(lua: *mut lua_State, reader: *mut c_void, size: *mut usize) -> *const c_char {
444 unsafe { (&mut *reader.cast::<F>())(&mut LuaState::from_ptr(lua)).map_or(ptr::null(), |fragment| {
445 *size = fragment.as_ref().len();
446 (&raw const fragment).cast()
447 }) }
448 }
449
450 unsafe { ffi::lua_load(self.0, middleware::<T, F>, (&raw mut reader).cast(), chunk_name.as_ptr()); }
451 }*/
452
453 /// If `condition` is `false`, [`LuaState::arg_error`] is called using `arg_n` and `extra_msg`.
454 /// Otherwise nothing happens.
455 ///
456 /// # Lua Errors
457 ///
458 /// Intentionally errors if `condition` is `false`.
459 #[inline]
460 pub fn arg_check(&self, condition: bool, arg_n: i32, extra_msg: &CStr) {
461 if !condition {
462 self.arg_error(arg_n, extra_msg)
463 }
464 }
465}