mlua/luau/
mod.rs

1use std::ffi::CStr;
2use std::os::raw::c_int;
3use std::ptr;
4
5use crate::chunk::ChunkMode;
6use crate::error::Result;
7use crate::function::Function;
8use crate::state::{callback_error_ext, ExtraData, Lua};
9use crate::traits::{FromLuaMulti, IntoLua};
10use crate::types::MaybeSend;
11
12pub use require::{NavigateError, Require, TextRequirer};
13
14// Since Luau has some missing standard functions, we re-implement them here
15
16impl Lua {
17    /// Create a custom Luau `require` function using provided [`Require`] implementation to find
18    /// and load modules.
19    #[cfg(any(feature = "luau", doc))]
20    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
21    pub fn create_require_function<R: Require + MaybeSend + 'static>(&self, require: R) -> Result<Function> {
22        require::create_require_function(self, require)
23    }
24
25    pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
26        let globals = self.globals();
27
28        globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
29        globals.raw_set("loadstring", self.create_c_function(lua_loadstring)?)?;
30
31        // Set `_VERSION` global to include version number
32        // The environment variable `LUAU_VERSION` set by the build script
33        if let Some(version) = ffi::luau_version() {
34            globals.raw_set("_VERSION", format!("Luau {version}"))?;
35        }
36
37        // Enable default `require` implementation
38        let require = self.create_require_function(require::TextRequirer::new())?;
39        self.globals().raw_set("require", require)?;
40
41        Ok(())
42    }
43}
44
45unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
46    let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
47    let option = CStr::from_ptr(option);
48    let arg = ffi::luaL_optinteger(state, 2, 0);
49    let is_sandboxed = (*ExtraData::get(state)).sandboxed;
50    match option.to_str() {
51        Ok("collect") if !is_sandboxed => {
52            ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
53            0
54        }
55        Ok("stop") if !is_sandboxed => {
56            ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
57            0
58        }
59        Ok("restart") if !is_sandboxed => {
60            ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
61            0
62        }
63        Ok("count") => {
64            let kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0) as ffi::lua_Number;
65            let kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0) as ffi::lua_Number;
66            ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
67            1
68        }
69        Ok("step") if !is_sandboxed => {
70            let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
71            ffi::lua_pushboolean(state, res);
72            1
73        }
74        Ok("isrunning") if !is_sandboxed => {
75            let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
76            ffi::lua_pushboolean(state, res);
77            1
78        }
79        _ => ffi::luaL_error(state, cstr!("collectgarbage called with invalid option")),
80    }
81}
82
83unsafe extern "C-unwind" fn lua_loadstring(state: *mut ffi::lua_State) -> c_int {
84    callback_error_ext(state, ptr::null_mut(), false, move |extra, nargs| {
85        let rawlua = (*extra).raw_lua();
86        let (chunk, chunk_name) =
87            <(String, Option<String>)>::from_stack_args(nargs, 1, Some("loadstring"), rawlua)?;
88        let chunk_name = chunk_name.as_deref().unwrap_or("=(loadstring)");
89        (rawlua.lua())
90            .load(chunk)
91            .set_name(chunk_name)
92            .set_mode(ChunkMode::Text)
93            .into_function()?
94            .push_into_stack(rawlua)?;
95        Ok(1)
96    })
97}
98
99mod require;