use std::cell::Cell;
use crate::error::{Error, Result};
use crate::sync::{MaybeSend, MaybeSync, NotSync, XRc, XWeak, NOT_SYNC};
use crate::sys::*;
use crate::value::Value;
pub use crate::gc::{GcGenParams, GcIncParams, GcMode};
pub(crate) struct LuaInner {
pub(crate) state: *mut lua_State,
owned: bool,
#[cfg(feature = "typecheck")]
typecheck_defs: std::cell::RefCell<String>,
}
impl LuaInner {
fn new(state: *mut lua_State, owned: bool) -> LuaInner {
LuaInner {
state,
owned,
#[cfg(feature = "typecheck")]
typecheck_defs: std::cell::RefCell::new(String::new()),
}
}
}
impl Drop for LuaInner {
fn drop(&mut self) {
if self.owned && !self.state.is_null() {
crate::app_data::clear_app_data(self.state);
unsafe {
crate::sys::lua_setmemcat(self.state, 0);
lua_close(self.state)
}
}
}
}
#[cfg(feature = "send")]
unsafe impl Send for LuaInner {}
#[cfg(feature = "send")]
unsafe impl Sync for LuaInner {}
#[derive(Clone)]
pub struct Lua {
pub(crate) inner: XRc<LuaInner>,
pub(crate) _not_sync: NotSync,
}
impl Lua {
pub fn new() -> Lua {
luaur_common::set_all_flags(true);
unsafe {
let state = lua_l_newstate();
assert!(!state.is_null(), "lua_l_newstate returned null");
lua_l_openlibs(state);
Lua {
inner: XRc::new(LuaInner::new(state, true)),
_not_sync: NOT_SYNC,
}
}
}
pub fn new_empty() -> Lua {
luaur_common::set_all_flags(true);
let state = lua_l_newstate();
assert!(!state.is_null(), "lua_l_newstate returned null");
Lua {
inner: XRc::new(LuaInner::new(state, true)),
_not_sync: NOT_SYNC,
}
}
pub unsafe fn unsafe_new() -> Lua {
Lua::new()
}
pub fn new_with(
libs: crate::options::StdLib,
options: crate::options::LuaOptions,
) -> Result<Lua> {
luaur_common::set_all_flags(true);
unsafe {
let state = lua_l_newstate();
assert!(!state.is_null(), "lua_l_newstate returned null");
if !libs.is_none() {
lua_l_openlibs(state);
}
let lua = Lua {
inner: XRc::new(LuaInner::new(state, true)),
_not_sync: NOT_SYNC,
};
lua.set_catch_rust_panics(options.catch_rust_panics);
Ok(lua)
}
}
#[inline]
pub(crate) fn state(&self) -> *mut lua_State {
self.inner.state
}
pub(crate) unsafe fn from_borrowed(state: *mut lua_State) -> Lua {
Lua {
inner: XRc::new(LuaInner::new(state, false)),
_not_sync: NOT_SYNC,
}
}
pub(crate) fn register_ref(&self, idx: c_int) -> LuaRef {
let id = unsafe { lua_ref(self.state(), idx) };
LuaRef {
inner: self.inner.clone(),
id: Cell::new(id),
}
}
pub(crate) fn pop_ref(&self) -> LuaRef {
let r = self.register_ref(-1);
unsafe { lua_pop(self.state(), 1) };
r
}
}
impl Default for Lua {
fn default() -> Self {
Lua::new()
}
}
impl Lua {
pub fn weak(&self) -> WeakLua {
WeakLua(XRc::downgrade(&self.inner))
}
}
#[derive(Clone)]
pub struct WeakLua(pub(crate) XWeak<LuaInner>);
impl WeakLua {
pub fn try_upgrade(&self) -> Option<Lua> {
self.0.upgrade().map(|inner| Lua {
inner,
_not_sync: NOT_SYNC,
})
}
pub fn upgrade(&self) -> Lua {
self.try_upgrade().expect("Lua instance is destroyed")
}
}
use crate::callback::{create_callback_function, BoxedCallback};
use crate::chunk::Chunk;
use crate::function::Function;
use crate::multi::MultiValue;
use crate::string::LuaString;
use crate::table::Table;
use crate::traits::{FromLuaMulti, IntoLuaMulti};
use crate::userdata::{AnyUserData, UserData};
impl Lua {
pub fn globals(&self) -> Table {
let state = self.state();
unsafe {
lua_pushvalue(state, LUA_GLOBALSINDEX);
Table::from_ref(self.pop_ref())
}
}
pub fn create_table(&self) -> Table {
crate::table::create_table(self)
}
pub fn create_table_result(&self) -> Result<Table> {
Ok(self.create_table())
}
pub fn create_string(&self, s: impl AsRef<[u8]>) -> LuaString {
crate::string::create_string(self, s.as_ref())
}
pub fn create_table_from<K, V, I>(&self, iter: I) -> Result<Table>
where
K: crate::traits::IntoLua,
V: crate::traits::IntoLua,
I: IntoIterator<Item = (K, V)>,
{
let t = self.create_table();
for (k, v) in iter {
t.raw_set(k, v)?;
}
Ok(t)
}
pub fn create_sequence_from<V, I>(&self, iter: I) -> Result<Table>
where
V: crate::traits::IntoLua,
I: IntoIterator<Item = V>,
{
let t = self.create_table();
for (i, v) in iter.into_iter().enumerate() {
t.raw_set((i + 1) as i64, v)?;
}
Ok(t)
}
pub fn gc_collect(&self) -> Result<()> {
lua_gc(self.state(), lua_GCOp::LUA_GCCOLLECT as c_int, 0);
Ok(())
}
pub fn create_function<F, A, R>(&self, func: F) -> Result<Function>
where
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let boxed: BoxedCallback = Box::new(move |lua, args| {
let a = A::from_lua_multi(args, lua)?;
let r = func(lua, a)?;
r.into_lua_multi(lua)
});
create_callback_function(self, boxed)
}
pub fn create_function_mut<F, A, R>(&self, func: F) -> Result<Function>
where
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let func = std::cell::RefCell::new(func);
self.create_function(move |lua, args| {
let mut borrow = func
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
(borrow)(lua, args)
})
}
pub fn create_userdata<T: UserData + MaybeSend + MaybeSync + 'static>(
&self,
data: T,
) -> Result<AnyUserData> {
crate::userdata::create_userdata(self, data)
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn create_async_function<F, A, FR, R>(&self, func: F) -> Result<Function>
where
F: Fn(Lua, A) -> FR + 'static,
A: FromLuaMulti,
FR: std::future::Future<Output = Result<R>> + 'static,
R: IntoLuaMulti,
{
let callback: crate::async_support::AsyncCallback = Box::new(move |lua, args| {
let a = A::from_lua_multi(args, &lua);
let fut = a.map(|a| func(lua.clone(), a));
Box::pin(async move {
let r = fut?.await?;
r.into_lua_multi(&lua)
})
});
crate::async_support::create_async_callback(self, callback)
}
pub fn create_buffer(&self, data: impl AsRef<[u8]>) -> Result<crate::buffer::Buffer> {
let data = data.as_ref();
let buffer = self.create_buffer_with_capacity(data.len())?;
if !data.is_empty() {
buffer.write_bytes(0, data);
}
Ok(buffer)
}
pub fn create_buffer_with_capacity(&self, size: usize) -> Result<crate::buffer::Buffer> {
crate::buffer::create_buffer_with_capacity(self, size)
}
pub fn create_vector(&self, x: f32, y: f32, z: f32) -> crate::vector::Vector {
crate::vector::Vector::new(x, y, z)
}
pub fn load(&self, source: impl AsRef<str>) -> Chunk {
Chunk {
lua: self.clone(),
source: source.as_ref().to_string(),
name: "chunk".to_string(),
environment: None,
compiler: None,
}
}
pub fn pack(&self, value: impl crate::traits::IntoLua) -> Result<crate::value::Value> {
value.into_lua(self)
}
pub fn pack_multi(&self, values: impl IntoLuaMulti) -> Result<MultiValue> {
values.into_lua_multi(self)
}
pub fn unpack_multi<T: FromLuaMulti>(&self, values: MultiValue) -> Result<T> {
T::from_lua_multi(values, self)
}
pub fn unpack<T: crate::traits::FromLua>(&self, value: Value) -> Result<T> {
T::from_lua(value, self)
}
pub fn coerce_integer(&self, value: Value) -> Result<Option<crate::value::Integer>> {
let state = self.state();
unsafe {
self.push_value(&value)?;
let mut isnum: c_int = 0;
let n = lua_tonumberx(state, -1, &mut isnum);
lua_pop(state, 1);
if isnum == 0 {
return Ok(None);
}
if n.fract() == 0.0 && n.is_finite() && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
Ok(Some(n as i64))
} else {
Ok(None)
}
}
}
pub fn coerce_number(&self, value: Value) -> Result<Option<crate::value::Number>> {
let state = self.state();
unsafe {
self.push_value(&value)?;
let mut isnum: c_int = 0;
let n = lua_tonumberx(state, -1, &mut isnum);
lua_pop(state, 1);
if isnum == 0 {
Ok(None)
} else {
Ok(Some(n))
}
}
}
pub fn set_globals(&self, globals: Table) -> Result<()> {
if self.is_sandboxed() {
return Err(Error::runtime(
"cannot change globals in a sandboxed Lua state",
));
}
let state = self.state();
unsafe {
globals.push_to_stack();
lua_replace(state, LUA_GLOBALSINDEX);
}
Ok(())
}
pub fn traceback(&self, msg: Option<&str>, level: usize) -> Result<LuaString> {
let state = self.state();
unsafe {
lua_l_traceback(state, state, msg, level as c_int);
Ok(LuaString::from_ref(self.pop_ref()))
}
}
}
#[cfg(feature = "typecheck")]
#[cfg_attr(docsrs, doc(cfg(feature = "typecheck")))]
impl Lua {
pub fn add_definitions(&self, defs: &str) -> Result<()> {
if let Err(diagnostics) = crate::typecheck::check_with_definitions("return nil", defs) {
let def_errors: Vec<crate::TypeDiagnostic> = diagnostics
.into_iter()
.filter(|d| d.in_definitions)
.collect();
if !def_errors.is_empty() {
return Err(Error::TypeError(def_errors));
}
}
let mut store = self.inner.typecheck_defs.borrow_mut();
if !store.is_empty() {
store.push('\n');
}
store.push_str(defs);
Ok(())
}
pub fn check(&self, source: &str) -> Result<()> {
let defs = self.inner.typecheck_defs.borrow();
let result = if defs.is_empty() {
crate::typecheck::check(source)
} else {
crate::typecheck::check_with_definitions(source, &defs)
};
result.map_err(Error::TypeError)
}
pub fn check_with_definitions(&self, source: &str, defs: &str) -> Result<()> {
let accumulated = self.inner.typecheck_defs.borrow();
let combined = if accumulated.is_empty() {
defs.to_string()
} else {
format!("{accumulated}\n{defs}")
};
crate::typecheck::check_with_definitions(source, &combined).map_err(Error::TypeError)
}
}
pub(crate) struct LuaRef {
inner: XRc<LuaInner>,
id: Cell<c_int>,
}
#[cfg(feature = "send")]
unsafe impl Send for LuaRef {}
#[cfg(feature = "send")]
unsafe impl Sync for LuaRef {}
impl LuaRef {
pub(crate) fn lua(&self) -> Lua {
Lua {
inner: self.inner.clone(),
_not_sync: NOT_SYNC,
}
}
#[inline]
pub(crate) fn state(&self) -> *mut lua_State {
self.inner.state
}
#[inline]
#[allow(dead_code)]
pub(crate) fn id(&self) -> c_int {
self.id.get()
}
pub(crate) fn push(&self) {
unsafe {
luaur_vm::functions::lua_rawgeti::lua_rawgeti(
self.state(),
luaur_vm::macros::lua_registryindex::LUA_REGISTRYINDEX,
self.id.get(),
);
}
}
}
impl Clone for LuaRef {
fn clone(&self) -> Self {
self.push();
let new = self.lua().pop_ref();
new
}
}
impl Drop for LuaRef {
fn drop(&mut self) {
let id = self.id.get();
if id > 0 && !self.inner.state.is_null() {
unsafe { lua_unref(self.inner.state, id) };
}
}
}
impl Lua {
pub(crate) fn value_from_stack(&self, idx: c_int) -> Result<Value> {
crate::value::value_from_stack(self, idx)
}
pub(crate) fn push_value(&self, value: &Value) -> Result<()> {
crate::value::push_value(self, value)
}
pub(crate) fn value_to_string(&self, value: &Value) -> Result<String> {
let state = self.state();
unsafe {
self.push_value(value)?;
let mut len = 0usize;
let p = lua_l_tolstring(state, -1, &mut len);
let out = if p.is_null() {
String::new()
} else {
let bytes = core::slice::from_raw_parts(p as *const u8, len);
String::from_utf8_lossy(bytes).into_owned()
};
lua_pop(state, 2);
Ok(out)
}
}
pub(crate) fn pop_error(&self, status: c_int) -> Error {
let state = self.state();
unsafe {
if let Some(cause) = crate::callback::recover_wrapped_error(state, -1) {
lua_pop(state, 1);
return Error::CallbackError {
traceback: String::new(),
cause: std::sync::Arc::new(cause),
};
}
let mut len = 0usize;
let s = lua_tolstring(state, -1, &mut len);
let msg = if s.is_null() {
"<non-string error>".to_string()
} else {
let bytes = core::slice::from_raw_parts(s as *const u8, len);
String::from_utf8_lossy(bytes).into_owned()
};
lua_pop(state, 1);
if status == 4 || msg == "not enough memory" {
return Error::MemoryError(msg);
}
Error::RuntimeError(msg)
}
}
}