Skip to main content

low_luajit/lua/type/
function.rs

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