Something went wrong. Try again.
Personal fork of prime agent. Mirror of: https://mnemosyne.sigilzero.dev/sigilzero/prime-agent
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720use std::cell::RefCell;use std::os::raw::{c_int, c_void};use std::{mem, ptr, slice};
use crate::error::{Error, Result};use crate::state::Lua;use crate::table::Table;use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};use crate::types::{Callback, LuaType, MaybeSend, ValueRef};use crate::util::{ assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,};use crate::value::Value;
#[cfg(feature = "async")]use { crate::thread::AsyncThread, crate::traits::LuaNativeAsyncFn, crate::types::AsyncCallback, std::future::{self, Future}, std::pin::{pin, Pin}, std::task::{Context, Poll},};
/// Handle to an internal Lua function.#[derive(Clone, Debug, PartialEq)]pub struct Function(pub(crate) ValueRef);
/// Contains information about a function.////// Please refer to the [`Lua Debug Interface`] for more information.////// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7#[derive(Clone, Debug)]#[non_exhaustive]pub struct FunctionInfo { /// A (reasonable) name of the function (`None` if the name cannot be found). pub name: Option<String>, /// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc). /// /// Always `None` for Luau. pub name_what: Option<&'static str>, /// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is /// the main part of a chunk. pub what: &'static str, /// Source of the chunk that created the function. pub source: Option<String>, /// A "printable" version of `source`, to be used in error messages. pub short_src: Option<String>, /// The line number where the definition of the function starts. pub line_defined: Option<usize>, /// The line number where the definition of the function ends (not set by Luau). pub last_line_defined: Option<usize>, /// The number of upvalues of the function. pub num_upvalues: u8, /// The number of parameters of the function (always 0 for C). #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))] #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))] pub num_params: u8, /// Whether the function is a variadic function (always true for C). #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))] #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))] pub is_vararg: bool,}
/// Luau function coverage snapshot.#[cfg(any(feature = "luau", doc))]#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]#[derive(Clone, Debug, PartialEq, Eq)]pub struct CoverageInfo { pub function: Option<String>, pub line_defined: i32, pub depth: i32, pub hits: Vec<i32>,}
impl Function { /// Calls the function, passing `args` as function arguments. /// /// The function's return values are converted to the generic type `R`. /// /// # Examples /// /// Call Lua's built-in `tostring` function: /// /// ``` /// # use mlua::{Function, Lua, Result}; /// # fn main() -> Result<()> { /// # let lua = Lua::new(); /// let globals = lua.globals(); /// /// let tostring: Function = globals.get("tostring")?; /// /// assert_eq!(tostring.call::<String>(123)?, "123"); /// /// # Ok(()) /// # } /// ``` /// /// Call a function with multiple arguments: /// /// ``` /// # use mlua::{Function, Lua, Result}; /// # fn main() -> Result<()> { /// # let lua = Lua::new(); /// let sum: Function = lua.load( /// r#" /// function(a, b) /// return a + b /// end /// "#).eval()?; /// /// assert_eq!(sum.call::<u32>((3, 4))?, 3 + 4); /// /// # Ok(()) /// # } /// ``` pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> { let lua = self.0.lua.lock(); let state = lua.state(); unsafe { let _sg = StackGuard::new(state); check_stack(state, 2)?;
// Push error handler lua.push_error_traceback(); let stack_start = ffi::lua_gettop(state); // Push function and the arguments lua.push_ref(&self.0); let nargs = args.push_into_stack_multi(&lua)?; // Call the function let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start); if ret != ffi::LUA_OK { return Err(pop_error(state, ret)); } // Get the results let nresults = ffi::lua_gettop(state) - stack_start; R::from_stack_multi(nresults, &lua) } }
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments, /// and drives the execution. /// /// Internally it wraps the function to an [`AsyncThread`]. The returned type implements /// `Future<Output = Result<R>>` and can be awaited. /// /// # Examples /// /// ``` /// use std::time::Duration; /// # use mlua::{Lua, Result}; /// # #[tokio::main] /// # async fn main() -> Result<()> { /// # let lua = Lua::new(); /// /// let sleep = lua.create_async_function(move |_lua, n: u64| async move { /// tokio::time::sleep(Duration::from_millis(n)).await; /// Ok(()) /// })?; /// /// sleep.call_async::<()>(10).await?; /// /// # Ok(()) /// # } /// ``` /// /// [`AsyncThread`]: crate::AsyncThread #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R> where R: FromLuaMulti, { let lua = self.0.lua.lock(); AsyncCallFuture(unsafe { lua.create_recycled_thread(self).and_then(|th| { let mut th = th.into_async(args)?; th.set_recyclable(true); Ok(th) }) }) }
/// Returns a function that, when called, calls `self`, passing `args` as the first set of /// arguments. /// /// If any arguments are passed to the returned function, they will be passed after `args`. /// /// # Examples /// /// ``` /// # use mlua::{Function, Lua, Result}; /// # fn main() -> Result<()> { /// # let lua = Lua::new(); /// let sum: Function = lua.load( /// r#" /// function(a, b) /// return a + b /// end /// "#).eval()?; /// /// let bound_a = sum.bind(1)?; /// assert_eq!(bound_a.call::<u32>(2)?, 1 + 2); /// /// let bound_a_and_b = sum.bind(13)?.bind(57)?; /// assert_eq!(bound_a_and_b.call::<u32>(())?, 13 + 57); /// /// # Ok(()) /// # } /// ``` pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> { unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int { let nargs = ffi::lua_gettop(state); let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int; ffi::luaL_checkstack(state, nbinds, ptr::null());
for i in 0..nbinds { ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2)); } if nargs > 0 { ffi::lua_rotate(state, 1, nbinds); }
nargs + nbinds }
let lua = self.0.lua.lock(); let state = lua.state();
let args = args.into_lua_multi(lua.lua())?; let nargs = args.len() as c_int;
if nargs == 0 { return Ok(self.clone()); }
if nargs + 1 > ffi::LUA_MAX_UPVALUES { return Err(Error::BindError); }
let args_wrapper = unsafe { let _sg = StackGuard::new(state); check_stack(state, nargs + 3)?;
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer); for arg in &args { lua.push_value(arg)?; } protect_lua!(state, nargs + 1, 1, fn(state) { ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state)); })?;
Function(lua.pop_ref()) };
let lua = lua.lua(); lua.load( r#" local func, args_wrapper = ... return function(...) return func(args_wrapper(...)) end "#, ) .try_cache() .set_name("=__mlua_bind") .call((self, args_wrapper)) }
/// Returns the environment of the Lua function. /// /// By default Lua functions shares a global environment. /// /// This function always returns `None` for Rust/C functions. pub fn environment(&self) -> Option<Table> { let lua = self.0.lua.lock(); let state = lua.state(); unsafe { let _sg = StackGuard::new(state); assert_stack(state, 1);
lua.push_ref(&self.0); if ffi::lua_iscfunction(state, -1) != 0 { return None; }
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] ffi::lua_getfenv(state, -1); #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))] for i in 1..=255 { // Traverse upvalues until we find the _ENV one match ffi::lua_getupvalue(state, -1, i) { s if s.is_null() => break, s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => break, _ => ffi::lua_pop(state, 1), } }
if ffi::lua_type(state, -1) != ffi::LUA_TTABLE { return None; } Some(Table(lua.pop_ref())) } }
/// Sets the environment of the Lua function. /// /// The environment is a table that is used as the global environment for the function. /// Returns `true` if environment successfully changed, `false` otherwise. /// /// This function does nothing for Rust/C functions. pub fn set_environment(&self, env: Table) -> Result<bool> { let lua = self.0.lua.lock(); let state = lua.state(); unsafe { let _sg = StackGuard::new(state); check_stack(state, 2)?;
lua.push_ref(&self.0); if ffi::lua_iscfunction(state, -1) != 0 { return Ok(false); }
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] { lua.push_ref(&env.0); ffi::lua_setfenv(state, -2); } #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))] for i in 1..=255 { match ffi::lua_getupvalue(state, -1, i) { s if s.is_null() => return Ok(false), s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => { ffi::lua_pop(state, 1); // Create an anonymous function with the new environment let f_with_env = lua .lua() .load("return _ENV") .set_environment(env) .try_cache() .into_function()?; lua.push_ref(&f_with_env.0); ffi::lua_upvaluejoin(state, -2, i, -1, 1); break; } _ => ffi::lua_pop(state, 1), } }
Ok(true) } }
/// Returns information about the function. /// /// Corresponds to the `>Snu` (`>Sn` for Luau) what mask for /// [`lua_getinfo`] when applied to the function. /// /// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo pub fn info(&self) -> FunctionInfo { let lua = self.0.lua.lock(); let state = lua.state(); unsafe { let _sg = StackGuard::new(state); assert_stack(state, 1);
let mut ar: ffi::lua_Debug = mem::zeroed(); lua.push_ref(&self.0);
#[cfg(not(feature = "luau"))] let res = ffi::lua_getinfo(state, cstr!(">Snu"), &mut ar); #[cfg(not(feature = "luau"))] mlua_assert!(res != 0, "lua_getinfo failed with `>Snu`");
#[cfg(feature = "luau")] let res = ffi::lua_getinfo(state, -1, cstr!("snau"), &mut ar); #[cfg(feature = "luau")] mlua_assert!(res != 0, "lua_getinfo failed with `snau`");
FunctionInfo { name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()), #[cfg(not(feature = "luau"))] name_what: match ptr_to_str(ar.namewhat) { Some("") => None, val => val, }, #[cfg(feature = "luau")] name_what: None, what: ptr_to_str(ar.what).unwrap_or("main"), source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()), #[cfg(not(feature = "luau"))] short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()), #[cfg(feature = "luau")] short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()), line_defined: linenumber_to_usize(ar.linedefined), #[cfg(not(feature = "luau"))] last_line_defined: linenumber_to_usize(ar.lastlinedefined), #[cfg(feature = "luau")] last_line_defined: None, #[cfg(not(feature = "luau"))] num_upvalues: ar.nups as _, #[cfg(feature = "luau")] num_upvalues: ar.nupvals, #[cfg(not(any(feature = "lua51", feature = "luajit")))] num_params: ar.nparams, #[cfg(not(any(feature = "lua51", feature = "luajit")))] is_vararg: ar.isvararg != 0, } } }
/// Dumps the function as a binary chunk. /// /// If `strip` is true, the binary representation may not include all debug information /// about the function, to save space. /// /// For Luau a [`Compiler`] can be used to compile Lua chunks to bytecode. /// /// [`Compiler`]: crate::chunk::Compiler #[cfg(not(feature = "luau"))] #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))] pub fn dump(&self, strip: bool) -> Vec<u8> { unsafe extern "C-unwind" fn writer( _state: *mut ffi::lua_State, buf: *const c_void, buf_len: usize, data_ptr: *mut c_void, ) -> c_int { // If `data` is null, then it's a signal that write is finished. if !data_ptr.is_null() && buf_len > 0 { let data = &mut *(data_ptr as *mut Vec<u8>); let buf = slice::from_raw_parts(buf as *const u8, buf_len); data.extend_from_slice(buf); } 0 }
let lua = self.0.lua.lock(); let state = lua.state(); let mut data: Vec<u8> = Vec::new(); unsafe { let _sg = StackGuard::new(state); assert_stack(state, 1);
lua.push_ref(&self.0); let data_ptr = &mut data as *mut Vec<u8> as *mut c_void; ffi::lua_dump(state, writer, data_ptr, strip as i32); ffi::lua_pop(state, 1); }
data }
/// Retrieves recorded coverage information about this Lua function including inner calls. /// /// This function takes a callback as an argument and calls it providing [`CoverageInfo`] /// snapshot per each executed inner function. /// /// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option. /// /// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level #[cfg(any(feature = "luau", doc))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))] pub fn coverage<F>(&self, func: F) where F: FnMut(CoverageInfo), { use std::ffi::CStr; use std::os::raw::c_char;
unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>( data: *mut c_void, function: *const c_char, line_defined: c_int, depth: c_int, hits: *const c_int, size: usize, ) { let function = if !function.is_null() { Some(CStr::from_ptr(function).to_string_lossy().to_string()) } else { None }; let rust_callback = &*(data as *const RefCell<F>); if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() { // Call the Rust callback with CoverageInfo rust_callback(CoverageInfo { function, line_defined, depth, hits: slice::from_raw_parts(hits, size).to_vec(), }); } }
let lua = self.0.lua.lock(); let state = lua.state(); unsafe { let _sg = StackGuard::new(state); assert_stack(state, 1);
lua.push_ref(&self.0); let func = RefCell::new(func); let func_ptr = &func as *const RefCell<F> as *mut c_void; ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>); } }
/// Converts this function to a generic C pointer. /// /// There is no way to convert the pointer back to its original value. /// /// Typically this function is used only for hashing and debug information. #[inline] pub fn to_pointer(&self) -> *const c_void { self.0.to_pointer() }
/// Creates a deep clone of the Lua function. /// /// Copies the function prototype and all its upvalues to the /// newly created function. /// This function returns shallow clone (same handle) for Rust/C functions. #[cfg(any(feature = "luau", doc))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))] pub fn deep_clone(&self) -> Result<Self> { let lua = self.0.lua.lock(); let state = lua.state(); unsafe { let _sg = StackGuard::new(state); check_stack(state, 2)?;
lua.push_ref(&self.0); if ffi::lua_iscfunction(state, -1) != 0 { return Ok(self.clone()); }
if lua.unlikely_memory_error() { ffi::lua_clonefunction(state, -1); } else { protect_lua!(state, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?; } Ok(Function(lua.pop_ref())) } }}
struct WrappedFunction(pub(crate) Callback);
#[cfg(feature = "async")]struct WrappedAsyncFunction(pub(crate) AsyncCallback);
impl Function { /// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] /// trait. #[inline] pub fn wrap<F, A, R>(func: F) -> impl IntoLua where F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { WrappedFunction(Box::new(move |lua, nargs| unsafe { let args = A::from_stack_args(nargs, 1, None, lua)?; func.call(args)?.push_into_stack_multi(lua) })) }
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait. pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua where F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let func = RefCell::new(func); WrappedFunction(Box::new(move |lua, nargs| unsafe { let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?; let args = A::from_stack_args(nargs, 1, None, lua)?; func.call(args)?.push_into_stack_multi(lua) })) }
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] /// trait. /// /// This function is similar to [`Function::wrap`] but any returned `Result` will be converted /// to a `ok, err` tuple without throwing an exception. #[inline] pub fn wrap_raw<F, A>(func: F) -> impl IntoLua where F: LuaNativeFn<A> + MaybeSend + 'static, A: FromLuaMulti, { WrappedFunction(Box::new(move |lua, nargs| unsafe { let args = A::from_stack_args(nargs, 1, None, lua)?; func.call(args).push_into_stack_multi(lua) })) }
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait. /// /// This function is similar to [`Function::wrap_mut`] but any returned `Result` will be /// converted to a `ok, err` tuple without throwing an exception. #[inline] pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua where F: LuaNativeFnMut<A> + MaybeSend + 'static, A: FromLuaMulti, { let func = RefCell::new(func); WrappedFunction(Box::new(move |lua, nargs| unsafe { let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?; let args = A::from_stack_args(nargs, 1, None, lua)?; func.call(args).push_into_stack_multi(lua) })) }
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] /// trait. #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua where F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe { let args = match A::from_stack_args(nargs, 1, None, rawlua) { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let lua = rawlua.lua(); let fut = func.call(args); Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) })) }
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] /// trait. /// /// This function is similar to [`Function::wrap_async`] but any returned `Result` will be /// converted to a `ok, err` tuple without throwing an exception. #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua where F: LuaNativeAsyncFn<A> + MaybeSend + 'static, A: FromLuaMulti, { WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe { let args = match A::from_stack_args(nargs, 1, None, rawlua) { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let lua = rawlua.lua(); let fut = func.call(args); Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) }) })) }}
impl IntoLua for WrappedFunction { #[inline] fn into_lua(self, lua: &Lua) -> Result<Value> { lua.lock().create_callback(self.0).map(Value::Function) }}
#[cfg(feature = "async")]impl IntoLua for WrappedAsyncFunction { #[inline] fn into_lua(self, lua: &Lua) -> Result<Value> { lua.lock().create_async_callback(self.0).map(Value::Function) }}
impl LuaType for Function { const TYPE_ID: c_int = ffi::LUA_TFUNCTION;}
#[cfg(feature = "async")]#[must_use = "futures do nothing unless you `.await` or poll them"]pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
#[cfg(feature = "async")]impl<R: FromLuaMulti> AsyncCallFuture<R> { pub(crate) fn error(err: Error) -> Self { AsyncCallFuture(Err(err)) }}
#[cfg(feature = "async")]impl<R: FromLuaMulti> Future for AsyncCallFuture<R> { type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { let this = self.get_mut(); match &mut this.0 { Ok(thread) => pin!(thread).poll(cx), Err(err) => Poll::Ready(Err(err.clone())), } }}
#[cfg(test)]mod assertions { use super::*;
#[cfg(not(feature = "send"))] static_assertions::assert_not_impl_any!(Function: Send); #[cfg(feature = "send")] static_assertions::assert_impl_all!(Function: Send, Sync);
#[cfg(all(feature = "async", feature = "send"))] static_assertions::assert_impl_all!(AsyncCallFuture<()>: Send);}