use core::alloc::Layout;
use core::cell::UnsafeCell;
use core::mem::{ManuallyDrop, size_of};
use core::ptr::{self, NonNull};
use crate::call::ProtectedCall;
use crate::function::FunctionRuntime;
use crate::function::{RawUpVal, RawUpValData, RawUpValOpen};
use crate::gc::{FIXED_BIT, GcRuntime, GcStats, WHITE0_BIT, bit_mask};
use crate::handle::RawHandle;
use crate::handle::sealed::Sealed;
use crate::memory::MemoryRuntime;
use crate::memory::{LUA_MEMORY_CATEGORIES, LUA_SIZE_CLASSES};
use crate::metamethod::TM_N;
use crate::state::ThreadState;
use crate::state::{
ExecutionCallbackStorage, LUA_EXECUTION_CALLBACK_STORAGE, LuaCallbacks, LuaExecutionCallbacks,
RawGlobalState, RawLuaState, RawMainState, THREAD_STATUS_OK, open_main_state,
};
use crate::state::{LuaAllocator, VmAllocator};
use crate::string::StringTable;
use crate::types::{LUA_T_COUNT, LUA_TTHREAD};
use crate::userdata::{LuaUserdataDirectAccessData, UserdataTypeRegistry};
use crate::value::RAW_TVALUE_NIL;
use crate::thread::Thread;
pub struct Lua {
thread: Thread,
allocator: NonNull<VmAllocator>,
}
impl Lua {
pub fn new() -> Option<Self> {
Self::new_with_vm_allocator(VmAllocator::system())
}
pub fn new_with_allocator<A: LuaAllocator + 'static>(allocator: A) -> Option<Self> {
Self::new_with_vm_allocator(VmAllocator::custom(allocator))
}
fn new_with_vm_allocator(allocator: VmAllocator) -> Option<Self> {
let allocator = Box::new(allocator);
let main_state =
unsafe { allocator.allocate(Layout::new::<RawMainState>()) }?.cast::<RawMainState>();
let allocator = unsafe { NonNull::new_unchecked(Box::into_raw(allocator)) };
unsafe {
let main_state = main_state.as_ptr();
let current_white = bit_mask(WHITE0_BIT) | bit_mask(FIXED_BIT);
let state_ptr = &raw mut (*main_state).state;
let global_ptr = &raw mut (*main_state).global;
let uv_head_ptr = &raw mut (*global_ptr).uv_head;
let mut memcat_bytes = [0; LUA_MEMORY_CATEGORIES];
memcat_bytes[0] = size_of::<RawMainState>();
ptr::write(
state_ptr,
RawLuaState {
tt: LUA_TTHREAD as u8,
marked: current_white,
memcat: 0,
status: THREAD_STATUS_OK,
active_memcat: 0,
is_active: false,
single_step: false,
top: ptr::null_mut(),
base: ptr::null_mut(),
global: global_ptr,
ci: ptr::null_mut(),
stack_last: ptr::null_mut(),
stack: ptr::null_mut(),
end_ci: ptr::null_mut(),
base_ci: ptr::null_mut(),
stack_size: 0,
size_ci: 0,
native_call_depth: 0,
base_native_call_depth: 0,
cached_slot: 0,
gt: ptr::null_mut(),
open_upval: ptr::null_mut(),
gc_list: ptr::null_mut(),
name_call: ptr::null_mut(),
userdata: ptr::null_mut(),
},
);
ptr::write(
global_ptr,
RawGlobalState {
string_table: StringTable {
hash: ptr::null_mut(),
n_use: 0,
size: 0,
},
allocator,
current_white,
gc_state: crate::gc::GCS_PAUSE,
gray: ptr::null_mut(),
gray_again: ptr::null_mut(),
weak: ptr::null_mut(),
gc_threshold: 0,
total_bytes: size_of::<RawMainState>(),
gc_goal: 200,
gc_step_mul: 200,
gc_step_size: 1024,
free_pages: [ptr::null_mut(); LUA_SIZE_CLASSES],
free_gco_pages: [ptr::null_mut(); LUA_SIZE_CLASSES],
all_pages: ptr::null_mut(),
all_gco_pages: ptr::null_mut(),
sweep_gco_page: ptr::null_mut(),
main_thread: state_ptr,
uv_head: RawUpVal {
tt: 0,
marked: 0,
memcat: 0,
marked_open: 0,
value: ptr::null_mut(),
data: RawUpValData {
open: ManuallyDrop::new(RawUpValOpen {
prev: uv_head_ptr,
next: uv_head_ptr,
thread_next: ptr::null_mut(),
}),
},
},
mt: [ptr::null_mut(); LUA_T_COUNT],
tt_name: [ptr::null_mut(); LUA_T_COUNT],
tm_name: [ptr::null_mut(); TM_N],
pseudo_temp: RAW_TVALUE_NIL,
registry: RAW_TVALUE_NIL,
registry_free: 0,
protected_error: ptr::null_mut(),
rng_state: 0,
ptr_enc_key: [1, 0, 0, 0],
cb: LuaCallbacks::default(),
ecb: LuaExecutionCallbacks::default(),
ecb_data: ExecutionCallbackStorage {
bytes: [0; LUA_EXECUTION_CALLBACK_STORAGE],
},
userdata_type_registry: UnsafeCell::new(UserdataTypeRegistry::new()),
userdata_direct: core::array::from_fn(|_| LuaUserdataDirectAccessData {
index_tm: RAW_TVALUE_NIL,
new_index_tm: RAW_TVALUE_NIL,
name_call_tm: RAW_TVALUE_NIL,
index: None,
new_index: None,
name_call: None,
}),
memcat_bytes,
userdata_gc: [None; crate::userdata::USERDATA_TAG_LIMIT],
userdata_mark: [None; crate::userdata::USERDATA_TAG_LIMIT],
userdata_mt: [ptr::null_mut(); crate::userdata::USERDATA_TAG_LIMIT],
weak_registry: RAW_TVALUE_NIL,
weak_registry_free: 0,
embedder_gc: None,
light_userdata_name: [ptr::null_mut();
crate::userdata::LIGHT_USERDATA_TAG_LIMIT],
userdata_direct_fields: [ptr::null_mut();
crate::userdata::USERDATA_INTERNAL_LIMIT],
gc_stats: GcStats::default(),
last_proto_id: 1,
},
);
let raw = NonNull::new_unchecked(state_ptr);
let thread = Thread::from_raw(raw);
let lua = Lua { thread, allocator };
let thread = lua.main_thread();
let mut unit = ();
thread
.raw_run_protected(open_main_state, &mut unit)
.is_ok()
.then_some(lua)
}
}
pub fn main_thread(&self) -> &Thread {
&self.thread
}
}
impl Sealed for Lua {}
impl RawHandle for Lua {
type Raw = RawLuaState;
fn as_ptr(&self) -> *mut Self::Raw {
self.thread.as_ptr()
}
}
impl AsRef<Lua> for Lua {
fn as_ref(&self) -> &Lua {
self
}
}
impl Drop for Lua {
fn drop(&mut self) {
unsafe {
let thread = self.main_thread();
let global = thread.global();
if let Some(stack) = thread.stack().value() {
thread.close(stack);
}
thread.free_all();
UserdataTypeRegistry::free(global.userdata_type_registry_ptr(), thread);
assert_eq!(
global
.as_ptr()
.as_ref()
.unwrap_unchecked()
.string_table
.n_use,
0
);
if !global
.as_ptr()
.as_ref()
.unwrap_unchecked()
.string_table
.hash
.is_null()
{
thread.free_array(
global
.as_ptr()
.as_ref()
.unwrap_unchecked()
.string_table
.hash,
global
.as_ptr()
.as_ref()
.unwrap_unchecked()
.string_table
.size
.max(0) as usize,
0,
);
}
thread.free_stack(thread);
for index in 0..LUA_SIZE_CLASSES {
assert!(global.as_ptr().as_ref().unwrap_unchecked().free_pages[index].is_null());
assert!(
global.as_ptr().as_ref().unwrap_unchecked().free_gco_pages[index].is_null()
);
}
assert!(
global
.as_ptr()
.as_ref()
.unwrap_unchecked()
.all_gco_pages
.is_null()
);
assert_eq!(
global.as_ptr().as_ref().unwrap_unchecked().total_bytes,
size_of::<RawMainState>()
);
assert_eq!(
global.as_ptr().as_ref().unwrap_unchecked().memcat_bytes[0],
size_of::<RawMainState>()
);
for index in 1..LUA_MEMORY_CATEGORIES {
assert_eq!(
global.as_ptr().as_ref().unwrap_unchecked().memcat_bytes[index],
0
);
}
if let Some(close) = global.execution_close() {
close(thread);
}
let allocator = Box::from_raw(self.allocator.as_ptr());
allocator.deallocate(
NonNull::new_unchecked(thread.as_ptr().cast()),
Layout::new::<RawMainState>(),
);
}
}
}