use core::cell::{Cell, RefCell};
use core::ptr::{self, NonNull};
use std::rc::Rc;
use luau_vm::Thread as VmThread;
use luau_vm::state::{GlobalState, LuaCallbacks};
use luau_vm::thread::LUA_GLOBALS_INDEX;
use crate::hooks::CallbackManager;
use crate::lua::Compiler;
use crate::lua::RegistryState;
use crate::lua::app_data::AppData;
use crate::lua::memory::MemoryState;
use crate::lua::sandbox::LuaSandboxState;
use crate::userdata::PendingUserdataRegistrations;
pub(crate) struct RuntimeData {
registry: RegistryState,
userdata_registrations: PendingUserdataRegistrations,
compiler: RefCell<Compiler>,
callbacks: CallbackManager,
memory: Rc<MemoryState>,
app_data: AppData,
global: GlobalState,
current_thread: Cell<Option<NonNull<VmThread>>>,
sandbox: Cell<LuaSandboxState>,
}
impl RuntimeData {
pub(crate) fn new(memory: Rc<MemoryState>, main_thread: &VmThread) -> Self {
let global = unsafe { main_thread.global() };
Self {
registry: RegistryState::default(),
userdata_registrations: PendingUserdataRegistrations::default(),
compiler: RefCell::new(Compiler::default()),
callbacks: CallbackManager::default(),
memory,
app_data: AppData::default(),
global,
current_thread: Cell::new(None),
sandbox: Cell::new(LuaSandboxState::Disabled),
}
}
pub(crate) fn install_callbacks(&self, callbacks: &mut LuaCallbacks) {
callbacks.userdata = ptr::from_ref(self).cast_mut().cast();
self.callbacks.install(callbacks);
}
pub(crate) fn from_thread(thread: &VmThread) -> &Self {
unsafe {
let userdata = (*thread.callbacks()).userdata;
debug_assert!(!userdata.is_null());
&*userdata.cast::<Self>()
}
}
pub(crate) const fn registry(&self) -> &RegistryState {
&self.registry
}
pub(crate) const fn userdata_registrations(&self) -> &PendingUserdataRegistrations {
&self.userdata_registrations
}
pub(crate) fn compiler(&self) -> Compiler {
self.compiler.borrow().clone()
}
pub(crate) fn set_compiler(&self, compiler: Compiler) {
*self.compiler.borrow_mut() = compiler;
}
pub(crate) const fn callbacks(&self) -> &CallbackManager {
&self.callbacks
}
pub(crate) fn callbacks_mut(&mut self) -> &mut CallbackManager {
&mut self.callbacks
}
pub(crate) fn memory(&self) -> &MemoryState {
&self.memory
}
pub(crate) const fn app_data(&self) -> &AppData {
&self.app_data
}
pub(super) fn is_main_thread(&self, thread: &VmThread) -> bool {
unsafe { self.global.main_thread().eq(thread) }
}
pub(super) fn sandbox_state(&self) -> LuaSandboxState {
self.sandbox.get()
}
pub(super) fn set_sandbox_state(&self, state: LuaSandboxState) {
self.sandbox.set(state);
}
pub(crate) fn invalidate_managed_safe_env(&self) {
if matches!(self.sandbox.get(), LuaSandboxState::Disabled) {
return;
}
unsafe {
self.global.main_thread().set_safe_env(LUA_GLOBALS_INDEX, 0);
if let Some(current) = self.current_thread.get() {
current.as_ref().set_safe_env(LUA_GLOBALS_INDEX, 0);
}
}
}
pub(crate) fn with_thread<R>(&self, thread: &VmThread, callback: impl FnOnce() -> R) -> R {
let previous = self.current_thread.replace(Some(NonNull::from(thread)));
let _scope = CurrentThreadScope {
current: &self.current_thread,
previous,
};
callback()
}
pub(crate) fn with_current_thread<R>(
&self,
callback: impl for<'thread> FnOnce(&'thread VmThread) -> R,
) -> R {
match self.current_thread.get() {
Some(current) => unsafe { callback(current.as_ref()) },
None => {
let main = unsafe { self.global.main_thread() };
callback(&main)
}
}
}
}
struct CurrentThreadScope<'runtime> {
current: &'runtime Cell<Option<NonNull<VmThread>>>,
previous: Option<NonNull<VmThread>>,
}
impl Drop for CurrentThreadScope<'_> {
fn drop(&mut self) {
self.current.set(self.previous);
}
}