Use static lua lifetime

This commit is contained in:
Filip Tibell 2023-02-11 12:39:39 +01:00
parent 709a69aa82
commit 6c49ef7e4a
No known key found for this signature in database
11 changed files with 74 additions and 79 deletions

View file

@ -5,7 +5,7 @@ use tokio::fs;
use crate::utils::table::TableBuilder;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)?
.with_async_function("readFile", fs_read_file)?
.with_async_function("readDir", fs_read_dir)?
@ -18,11 +18,11 @@ pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
.build_readonly()
}
async fn fs_read_file(_: &Lua, path: String) -> LuaResult<String> {
async fn fs_read_file(_: &'static Lua, path: String) -> LuaResult<String> {
fs::read_to_string(&path).await.map_err(LuaError::external)
}
async fn fs_read_dir(_: &Lua, path: String) -> LuaResult<Vec<String>> {
async fn fs_read_dir(_: &'static Lua, path: String) -> LuaResult<Vec<String>> {
let mut dir_strings = Vec::new();
let mut dir = fs::read_dir(&path).await.map_err(LuaError::external)?;
while let Some(dir_entry) = dir.next_entry().await.map_err(LuaError::external)? {
@ -52,25 +52,25 @@ async fn fs_read_dir(_: &Lua, path: String) -> LuaResult<Vec<String>> {
Ok(dir_strings_no_prefix)
}
async fn fs_write_file(_: &Lua, (path, contents): (String, String)) -> LuaResult<()> {
async fn fs_write_file(_: &'static Lua, (path, contents): (String, String)) -> LuaResult<()> {
fs::write(&path, &contents)
.await
.map_err(LuaError::external)
}
async fn fs_write_dir(_: &Lua, path: String) -> LuaResult<()> {
async fn fs_write_dir(_: &'static Lua, path: String) -> LuaResult<()> {
fs::create_dir_all(&path).await.map_err(LuaError::external)
}
async fn fs_remove_file(_: &Lua, path: String) -> LuaResult<()> {
async fn fs_remove_file(_: &'static Lua, path: String) -> LuaResult<()> {
fs::remove_file(&path).await.map_err(LuaError::external)
}
async fn fs_remove_dir(_: &Lua, path: String) -> LuaResult<()> {
async fn fs_remove_dir(_: &'static Lua, path: String) -> LuaResult<()> {
fs::remove_dir_all(&path).await.map_err(LuaError::external)
}
async fn fs_is_file(_: &Lua, path: String) -> LuaResult<bool> {
async fn fs_is_file(_: &'static Lua, path: String) -> LuaResult<bool> {
let path = PathBuf::from(path);
if path.exists() {
Ok(fs::metadata(path)
@ -82,7 +82,7 @@ async fn fs_is_file(_: &Lua, path: String) -> LuaResult<bool> {
}
}
async fn fs_is_dir(_: &Lua, path: String) -> LuaResult<bool> {
async fn fs_is_dir(_: &'static Lua, path: String) -> LuaResult<bool> {
let path = PathBuf::from(path);
if path.exists() {
Ok(fs::metadata(path)

View file

@ -78,7 +78,7 @@ impl LuneGlobal {
Note that proxy globals should be handled with special care and that [`LuneGlobal::inject()`]
should be preferred over manually creating and manipulating the value(s) of any Lune global.
*/
pub fn value<'a>(&'a self, lua: &'a Lua) -> LuaResult<LuaTable> {
pub fn value(&self, lua: &'static Lua) -> LuaResult<LuaTable> {
match self {
LuneGlobal::Fs => fs::create(lua),
LuneGlobal::Net => net::create(lua),
@ -98,7 +98,7 @@ impl LuneGlobal {
Refer to [`LuneGlobal::is_top_level()`] for more info on proxy globals.
*/
pub fn inject(self, lua: &Lua) -> LuaResult<()> {
pub fn inject(self, lua: &'static Lua) -> LuaResult<()> {
let globals = lua.globals();
let table = self.value(lua)?;
// NOTE: Top level globals are special, the values

View file

@ -19,7 +19,7 @@ use crate::utils::{
table::TableBuilder,
};
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
// Create a reusable client for performing our
// web requests and store it in the lua registry
let mut default_headers = HeaderMap::new();
@ -43,7 +43,7 @@ pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
.build_readonly()
}
fn net_json_encode(_: &Lua, (val, pretty): (LuaValue, Option<bool>)) -> LuaResult<String> {
fn net_json_encode(_: &'static Lua, (val, pretty): (LuaValue, Option<bool>)) -> LuaResult<String> {
if let Some(true) = pretty {
serde_json::to_string_pretty(&val).map_err(LuaError::external)
} else {
@ -51,12 +51,12 @@ fn net_json_encode(_: &Lua, (val, pretty): (LuaValue, Option<bool>)) -> LuaResul
}
}
fn net_json_decode(lua: &Lua, json: String) -> LuaResult<LuaValue> {
fn net_json_decode(lua: &'static Lua, json: String) -> LuaResult<LuaValue> {
let json: serde_json::Value = serde_json::from_str(&json).map_err(LuaError::external)?;
lua.to_value(&json)
}
async fn net_request<'lua>(lua: &'lua Lua, config: LuaValue<'lua>) -> LuaResult<LuaTable<'lua>> {
async fn net_request<'a>(lua: &'static Lua, config: LuaValue<'a>) -> LuaResult<LuaTable<'a>> {
let client: NetClient = lua.named_registry_value("NetClient")?;
// Extract stuff from config and make sure its all valid
let (url, method, headers, body) = match config {
@ -147,20 +147,16 @@ async fn net_request<'lua>(lua: &'lua Lua, config: LuaValue<'lua>) -> LuaResult<
.build_readonly()
}
async fn net_serve<'lua>(
lua: &'lua Lua,
(port, callback): (u16, LuaFunction<'lua>),
) -> LuaResult<()> {
let server_lua = lua.app_data_ref::<Weak<Lua>>().unwrap().upgrade().unwrap();
async fn net_serve(lua: &'static Lua, (port, callback): (u16, LuaFunction<'_>)) -> LuaResult<()> {
let server_sender = lua
.app_data_ref::<Weak<Sender<LuneMessage>>>()
.unwrap()
.upgrade()
.unwrap();
let server_callback = server_lua.create_registry_value(callback)?;
let server_callback = lua.create_registry_value(callback)?;
let server = Server::bind(&([127, 0, 0, 1], port).into())
.executor(LocalExec)
.serve(MakeNetService(server_lua, server_callback.into()));
.serve(MakeNetService(lua, server_callback.into()));
if let Err(err) = server.await.map_err(LuaError::external) {
server_sender
.send(LuneMessage::LuaError(err))
@ -173,7 +169,7 @@ async fn net_serve<'lua>(
// Hyper service implementation for net, lots of boilerplate here
// but make_svc and make_svc_function do not work for what we need
pub struct NetService(Arc<Lua>, Arc<LuaRegistryKey>);
pub struct NetService(&'static Lua, Arc<LuaRegistryKey>);
impl Service<Request<Body>> for NetService {
type Response = Response<Body>;
@ -185,7 +181,7 @@ impl Service<Request<Body>> for NetService {
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
let lua = self.0.clone();
let lua = self.0;
let key = self.1.clone();
let (parts, body) = req.into_parts();
Box::pin(async move {
@ -199,7 +195,7 @@ impl Service<Request<Body>> for NetService {
.upgrade()
.unwrap();
// Create a readonly table for the request query params
let query_params = TableBuilder::new(&lua)?
let query_params = TableBuilder::new(lua)?
.with_values(
parts
.uri
@ -211,7 +207,7 @@ impl Service<Request<Body>> for NetService {
)?
.build_readonly()?;
// Do the same for headers
let header_map = TableBuilder::new(&lua)?
let header_map = TableBuilder::new(lua)?
.with_values(
parts
.headers
@ -223,7 +219,7 @@ impl Service<Request<Body>> for NetService {
)?
.build_readonly()?;
// Create a readonly table with request info to pass to the handler
let request = TableBuilder::new(&lua)?
let request = TableBuilder::new(lua)?
.with_value("path", parts.uri.path())?
.with_value("query", query_params)?
.with_value("method", parts.method.as_str())?
@ -287,7 +283,7 @@ impl Service<Request<Body>> for NetService {
}
}
struct MakeNetService(Arc<Lua>, Arc<LuaRegistryKey>);
struct MakeNetService(&'static Lua, Arc<LuaRegistryKey>);
impl Service<&AddrStream> for MakeNetService {
type Response = NetService;
@ -299,7 +295,7 @@ impl Service<&AddrStream> for MakeNetService {
}
fn call(&mut self, _: &AddrStream) -> Self::Future {
let lua = self.0.clone();
let lua = self.0;
let key = self.1.clone();
Box::pin(async move { Ok(NetService(lua, key)) })
}

View file

@ -10,7 +10,7 @@ use crate::utils::{
table::TableBuilder,
};
pub fn create(lua: &Lua, args_vec: Vec<String>) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua, args_vec: Vec<String>) -> LuaResult<LuaTable> {
let cwd = env::current_dir()?.canonicalize()?;
let mut cwd_str = cwd.to_string_lossy().to_string();
if !cwd_str.ends_with('/') {
@ -40,10 +40,10 @@ pub fn create(lua: &Lua, args_vec: Vec<String>) -> LuaResult<LuaTable> {
.build_readonly()
}
fn process_env_get<'lua>(
lua: &'lua Lua,
(_, key): (LuaValue<'lua>, String),
) -> LuaResult<LuaValue<'lua>> {
fn process_env_get<'a>(
lua: &'static Lua,
(_, key): (LuaValue<'a>, String),
) -> LuaResult<LuaValue<'a>> {
match env::var_os(key) {
Some(value) => {
let raw_value = RawOsString::new(value);
@ -55,7 +55,10 @@ fn process_env_get<'lua>(
}
}
fn process_env_set(_: &Lua, (_, key, value): (LuaValue, String, Option<String>)) -> LuaResult<()> {
fn process_env_set(
_: &'static Lua,
(_, key, value): (LuaValue, String, Option<String>),
) -> LuaResult<()> {
// Make sure key is valid, otherwise set_var will panic
if key.is_empty() {
Err(LuaError::RuntimeError("Key must not be empty".to_string()))
@ -106,12 +109,12 @@ fn process_env_iter<'lua>(
})
}
async fn process_exit(lua: &Lua, exit_code: Option<u8>) -> LuaResult<()> {
async fn process_exit(lua: &'static Lua, exit_code: Option<u8>) -> LuaResult<()> {
exit_and_yield_forever(lua, exit_code).await
}
async fn process_spawn<'a>(
lua: &'a Lua,
lua: &'static Lua,
(mut program, args, options): (String, Option<Vec<String>>, Option<LuaTable<'a>>),
) -> LuaResult<LuaTable<'a>> {
// Parse any given options or create defaults

View file

@ -9,7 +9,7 @@ use os_str_bytes::{OsStrBytes, RawOsStr};
use crate::utils::table::TableBuilder;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
let require: LuaFunction = lua.globals().raw_get("require")?;
// Preserve original require behavior if we have a special env var set
if env::var_os("LUAU_PWD_REQUIRE").is_some() {

View file

@ -8,7 +8,7 @@ use crate::utils::{
table::TableBuilder,
};
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)?
.with_function("color", |_, color: String| {
let ansi_string = format_style(style_from_color_str(&color)?);
@ -38,7 +38,7 @@ fn prompt_theme() -> ColorfulTheme {
}
fn prompt<'a>(
lua: &'a Lua,
lua: &'static Lua,
(kind, message, options): (Option<String>, Option<String>, LuaValue<'a>),
) -> LuaResult<LuaValue<'a>> {
match kind.map(|k| k.trim().to_ascii_lowercase()).as_deref() {

View file

@ -1,7 +1,4 @@
use std::{
sync::Weak,
time::{Duration, Instant},
};
use std::time::{Duration, Instant};
use mlua::prelude::*;
use tokio::time;
@ -13,7 +10,7 @@ use crate::utils::{
const MINIMUM_WAIT_OR_DELAY_DURATION: f32 = 10.0 / 1_000.0; // 10ms
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
// HACK: There is no way to call coroutine.close directly from the mlua
// crate, so we need to fetch the function and store it in the registry
let coroutine: LuaTable = lua.globals().raw_get("coroutine")?;
@ -33,8 +30,11 @@ pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
.build_readonly()
}
fn tof_to_thread<'a>(lua: &'a Lua, tof: LuaValue<'a>) -> LuaResult<LuaThread<'a>> {
match tof {
fn tof_to_thread<'a>(
lua: &'static Lua,
thread_or_function: LuaValue<'a>,
) -> LuaResult<LuaThread<'a>> {
match thread_or_function {
LuaValue::Thread(t) => Ok(t),
LuaValue::Function(f) => Ok(lua.create_thread(f)?),
value => Err(LuaError::RuntimeError(format!(
@ -44,31 +44,29 @@ fn tof_to_thread<'a>(lua: &'a Lua, tof: LuaValue<'a>) -> LuaResult<LuaThread<'a>
}
}
async fn task_cancel<'a>(lua: &'a Lua, thread: LuaThread<'a>) -> LuaResult<()> {
async fn task_cancel<'a>(lua: &'static Lua, thread: LuaThread<'a>) -> LuaResult<()> {
let close: LuaFunction = lua.named_registry_value("coroutine.close")?;
close.call_async::<_, LuaMultiValue>(thread).await?;
Ok(())
}
async fn task_defer<'a>(
lua: &'a Lua,
lua: &'static Lua,
(tof, args): (LuaValue<'a>, LuaMultiValue<'a>),
) -> LuaResult<LuaThread<'a>> {
// Spawn a new detached task using a lua reference that we can use inside of our task
let task_lua = lua.app_data_ref::<Weak<Lua>>().unwrap().upgrade().unwrap();
let task_thread = tof_to_thread(lua, tof)?;
let task_thread_key = lua.create_registry_value(task_thread)?;
let task_args_key = lua.create_registry_value(args.into_vec())?;
let lua_thread_to_return = lua.registry_value(&task_thread_key)?;
run_registered_task(lua, TaskRunMode::Deferred, async move {
let thread: LuaThread = task_lua.registry_value(&task_thread_key)?;
let argsv: Vec<LuaValue> = task_lua.registry_value(&task_args_key)?;
let thread: LuaThread = lua.registry_value(&task_thread_key)?;
let argsv: Vec<LuaValue> = lua.registry_value(&task_args_key)?;
let args = LuaMultiValue::from_vec(argsv);
if thread.status() == LuaThreadStatus::Resumable {
let _: LuaMultiValue = thread.into_async(args).await?;
}
task_lua.remove_registry_value(task_thread_key)?;
task_lua.remove_registry_value(task_args_key)?;
lua.remove_registry_value(task_thread_key)?;
lua.remove_registry_value(task_args_key)?;
Ok(())
})
.await?;
@ -76,25 +74,23 @@ async fn task_defer<'a>(
}
async fn task_delay<'a>(
lua: &'a Lua,
lua: &'static Lua,
(duration, tof, args): (Option<f32>, LuaValue<'a>, LuaMultiValue<'a>),
) -> LuaResult<LuaThread<'a>> {
// Spawn a new detached task using a lua reference that we can use inside of our task
let task_lua = lua.app_data_ref::<Weak<Lua>>().unwrap().upgrade().unwrap();
let task_thread = tof_to_thread(lua, tof)?;
let task_thread_key = lua.create_registry_value(task_thread)?;
let task_args_key = lua.create_registry_value(args.into_vec())?;
let lua_thread_to_return = lua.registry_value(&task_thread_key)?;
run_registered_task(lua, TaskRunMode::Deferred, async move {
task_wait(&task_lua, duration).await?;
let thread: LuaThread = task_lua.registry_value(&task_thread_key)?;
let argsv: Vec<LuaValue> = task_lua.registry_value(&task_args_key)?;
task_wait(lua, duration).await?;
let thread: LuaThread = lua.registry_value(&task_thread_key)?;
let argsv: Vec<LuaValue> = lua.registry_value(&task_args_key)?;
let args = LuaMultiValue::from_vec(argsv);
if thread.status() == LuaThreadStatus::Resumable {
let _: LuaMultiValue = thread.into_async(args).await?;
}
task_lua.remove_registry_value(task_thread_key)?;
task_lua.remove_registry_value(task_args_key)?;
lua.remove_registry_value(task_thread_key)?;
lua.remove_registry_value(task_args_key)?;
Ok(())
})
.await?;
@ -102,31 +98,29 @@ async fn task_delay<'a>(
}
async fn task_spawn<'a>(
lua: &'a Lua,
lua: &'static Lua,
(tof, args): (LuaValue<'a>, LuaMultiValue<'a>),
) -> LuaResult<LuaThread<'a>> {
// Spawn a new detached task using a lua reference that we can use inside of our task
let task_lua = lua.app_data_ref::<Weak<Lua>>().unwrap().upgrade().unwrap();
let task_thread = tof_to_thread(lua, tof)?;
let task_thread_key = lua.create_registry_value(task_thread)?;
let task_args_key = lua.create_registry_value(args.into_vec())?;
let lua_thread_to_return = lua.registry_value(&task_thread_key)?;
run_registered_task(lua, TaskRunMode::Instant, async move {
let thread: LuaThread = task_lua.registry_value(&task_thread_key)?;
let argsv: Vec<LuaValue> = task_lua.registry_value(&task_args_key)?;
let thread: LuaThread = lua.registry_value(&task_thread_key)?;
let argsv: Vec<LuaValue> = lua.registry_value(&task_args_key)?;
let args = LuaMultiValue::from_vec(argsv);
if thread.status() == LuaThreadStatus::Resumable {
let _: LuaMultiValue = thread.into_async(args).await?;
}
task_lua.remove_registry_value(task_thread_key)?;
task_lua.remove_registry_value(task_args_key)?;
lua.remove_registry_value(task_thread_key)?;
lua.remove_registry_value(task_args_key)?;
Ok(())
})
.await?;
Ok(lua_thread_to_return)
}
async fn task_wait(lua: &Lua, duration: Option<f32>) -> LuaResult<f32> {
async fn task_wait(lua: &'static Lua, duration: Option<f32>) -> LuaResult<f32> {
let start = Instant::now();
run_registered_task(lua, TaskRunMode::Blocking, async move {
time::sleep(Duration::from_secs_f32(

View file

@ -5,7 +5,7 @@ use crate::utils::{
table::TableBuilder,
};
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
let globals = lua.globals();
// HACK: We need to preserve the default behavior of the
// print and error functions, for pcall and such, which

View file

@ -76,6 +76,10 @@ impl Lune {
Some Lune globals such as [`LuneGlobal::Process`] may spawn
separate tokio tasks on other threads, but the Luau environment
itself is guaranteed to run on a single thread in the local set.
Note that this will create a static Lua instance that will live
for the remainer of the program, and that this leaks memory using
[`Box::leak`] that will then get deallocated when the program exits.
*/
pub async fn run(
&self,
@ -84,18 +88,16 @@ impl Lune {
) -> Result<ExitCode, LuaError> {
let task_set = task::LocalSet::new();
let (sender, mut receiver) = mpsc::channel::<LuneMessage>(64);
let lua = Arc::new(mlua::Lua::new());
let lua = Lua::new().into_static();
let snd = Arc::new(sender);
lua.set_app_data(Arc::downgrade(&lua));
lua.set_app_data(Arc::downgrade(&snd));
// Add in wanted lune globals
for global in self.includes.clone() {
if !self.excludes.contains(&global) {
global.inject(&lua)?;
global.inject(lua)?;
}
}
// Spawn the main thread from our entrypoint script
let script_lua = lua.clone();
let script_name = script_name.to_string();
let script_chunk = script_contents.to_string();
let script_sender = snd.clone();
@ -104,7 +106,7 @@ impl Lune {
.await
.map_err(LuaError::external)?;
task_set.spawn_local(async move {
let result = script_lua
let result = lua
.load(&script_chunk)
.set_name(&format!("={script_name}"))
.unwrap()

View file

@ -41,7 +41,7 @@ pub async fn pipe_and_inherit_child_process_stdio(
Ok::<_, LuaError>((status, stdout_buffer?, stderr_buffer?))
}
pub async fn exit_and_yield_forever(lua: &Lua, exit_code: Option<u8>) -> LuaResult<()> {
pub async fn exit_and_yield_forever(lua: &'static Lua, exit_code: Option<u8>) -> LuaResult<()> {
let sender = lua
.app_data_ref::<Weak<Sender<LuneMessage>>>()
.unwrap()

View file

@ -26,7 +26,7 @@ impl fmt::Display for TaskRunMode {
}
pub async fn run_registered_task<T>(
lua: &Lua,
lua: &'static Lua,
mode: TaskRunMode,
to_run: impl Future<Output = LuaResult<T>> + 'static,
) -> LuaResult<()> {