1use core::alloc::Layout;
2use core::cell::UnsafeCell;
3use core::mem::{ManuallyDrop, size_of};
4use core::ptr::{self, NonNull};
5
6use crate::call::ProtectedCall;
7use crate::function::FunctionRuntime;
8use crate::function::{RawUpVal, RawUpValData, RawUpValOpen};
9use crate::gc::{FIXED_BIT, GcRuntime, GcStats, WHITE0_BIT, bit_mask};
10use crate::handle::RawHandle;
11use crate::handle::sealed::Sealed;
12use crate::memory::MemoryRuntime;
13use crate::memory::{LUA_MEMORY_CATEGORIES, LUA_SIZE_CLASSES};
14use crate::metamethod::TM_N;
15use crate::state::ThreadState;
16use crate::state::{
17 ExecutionCallbackStorage, LUA_EXECUTION_CALLBACK_STORAGE, LuaCallbacks, LuaExecutionCallbacks,
18 RawGlobalState, RawLuaState, RawMainState, THREAD_STATUS_OK, open_main_state,
19};
20use crate::state::{LuaAllocator, VmAllocator};
21use crate::string::StringTable;
22use crate::types::{LUA_T_COUNT, LUA_TTHREAD};
23use crate::userdata::{LuaUserdataDirectAccessData, UserdataTypeRegistry};
24use crate::value::RAW_TVALUE_NIL;
25
26use crate::thread::Thread;
27
28pub struct Lua {
29 thread: Thread,
30 allocator: NonNull<VmAllocator>,
31}
32
33impl Lua {
34 pub fn new() -> Option<Self> {
36 Self::new_with_vm_allocator(VmAllocator::system())
37 }
38
39 pub fn new_with_allocator<A: LuaAllocator + 'static>(allocator: A) -> Option<Self> {
40 Self::new_with_vm_allocator(VmAllocator::custom(allocator))
41 }
42
43 fn new_with_vm_allocator(allocator: VmAllocator) -> Option<Self> {
44 let allocator = Box::new(allocator);
45 let main_state =
46 unsafe { allocator.allocate(Layout::new::<RawMainState>()) }?.cast::<RawMainState>();
47 let allocator = unsafe { NonNull::new_unchecked(Box::into_raw(allocator)) };
48
49 unsafe {
50 let main_state = main_state.as_ptr();
51 let current_white = bit_mask(WHITE0_BIT) | bit_mask(FIXED_BIT);
52 let state_ptr = &raw mut (*main_state).state;
53 let global_ptr = &raw mut (*main_state).global;
54 let uv_head_ptr = &raw mut (*global_ptr).uv_head;
55
56 let mut memcat_bytes = [0; LUA_MEMORY_CATEGORIES];
57 memcat_bytes[0] = size_of::<RawMainState>();
58
59 ptr::write(
60 state_ptr,
61 RawLuaState {
62 tt: LUA_TTHREAD as u8,
63 marked: current_white,
64 memcat: 0,
65 status: THREAD_STATUS_OK,
66 active_memcat: 0,
67 is_active: false,
68 single_step: false,
69 top: ptr::null_mut(),
70 base: ptr::null_mut(),
71 global: global_ptr,
72 ci: ptr::null_mut(),
73 stack_last: ptr::null_mut(),
74 stack: ptr::null_mut(),
75 end_ci: ptr::null_mut(),
76 base_ci: ptr::null_mut(),
77 stack_size: 0,
78 size_ci: 0,
79 native_call_depth: 0,
80 base_native_call_depth: 0,
81 cached_slot: 0,
82 gt: ptr::null_mut(),
83 open_upval: ptr::null_mut(),
84 gc_list: ptr::null_mut(),
85 name_call: ptr::null_mut(),
86 userdata: ptr::null_mut(),
87 },
88 );
89
90 ptr::write(
91 global_ptr,
92 RawGlobalState {
93 string_table: StringTable {
94 hash: ptr::null_mut(),
95 n_use: 0,
96 size: 0,
97 },
98 allocator,
99 current_white,
100 gc_state: crate::gc::GCS_PAUSE,
101 gray: ptr::null_mut(),
102 gray_again: ptr::null_mut(),
103 weak: ptr::null_mut(),
104 gc_threshold: 0,
105 total_bytes: size_of::<RawMainState>(),
106 gc_goal: 200,
107 gc_step_mul: 200,
108 gc_step_size: 1024,
109 free_pages: [ptr::null_mut(); LUA_SIZE_CLASSES],
110 free_gco_pages: [ptr::null_mut(); LUA_SIZE_CLASSES],
111 all_pages: ptr::null_mut(),
112 all_gco_pages: ptr::null_mut(),
113 sweep_gco_page: ptr::null_mut(),
114 main_thread: state_ptr,
115 uv_head: RawUpVal {
116 tt: 0,
117 marked: 0,
118 memcat: 0,
119 marked_open: 0,
120 value: ptr::null_mut(),
121 data: RawUpValData {
122 open: ManuallyDrop::new(RawUpValOpen {
123 prev: uv_head_ptr,
124 next: uv_head_ptr,
125 thread_next: ptr::null_mut(),
126 }),
127 },
128 },
129 mt: [ptr::null_mut(); LUA_T_COUNT],
130 tt_name: [ptr::null_mut(); LUA_T_COUNT],
131 tm_name: [ptr::null_mut(); TM_N],
132 pseudo_temp: RAW_TVALUE_NIL,
133 registry: RAW_TVALUE_NIL,
134 registry_free: 0,
135 protected_error: ptr::null_mut(),
136 rng_state: 0,
137 ptr_enc_key: [1, 0, 0, 0],
138 cb: LuaCallbacks::default(),
139 ecb: LuaExecutionCallbacks::default(),
140 ecb_data: ExecutionCallbackStorage {
141 bytes: [0; LUA_EXECUTION_CALLBACK_STORAGE],
142 },
143 userdata_type_registry: UnsafeCell::new(UserdataTypeRegistry::new()),
144 userdata_direct: core::array::from_fn(|_| LuaUserdataDirectAccessData {
145 index_tm: RAW_TVALUE_NIL,
146 new_index_tm: RAW_TVALUE_NIL,
147 name_call_tm: RAW_TVALUE_NIL,
148 index: None,
149 new_index: None,
150 name_call: None,
151 }),
152 memcat_bytes,
153 userdata_gc: [None; crate::userdata::USERDATA_TAG_LIMIT],
154 userdata_mark: [None; crate::userdata::USERDATA_TAG_LIMIT],
155 userdata_mt: [ptr::null_mut(); crate::userdata::USERDATA_TAG_LIMIT],
156 weak_registry: RAW_TVALUE_NIL,
157 weak_registry_free: 0,
158 embedder_gc: None,
159 light_userdata_name: [ptr::null_mut();
160 crate::userdata::LIGHT_USERDATA_TAG_LIMIT],
161 userdata_direct_fields: [ptr::null_mut();
162 crate::userdata::USERDATA_INTERNAL_LIMIT],
163 gc_stats: GcStats::default(),
164 last_proto_id: 1,
165 },
166 );
167
168 let raw = NonNull::new_unchecked(state_ptr);
169 let thread = Thread::from_raw(raw);
170 let lua = Lua { thread, allocator };
171 let thread = lua.main_thread();
172
173 let mut unit = ();
174 thread
175 .raw_run_protected(open_main_state, &mut unit)
176 .is_ok()
177 .then_some(lua)
178 }
179 }
180
181 pub fn main_thread(&self) -> &Thread {
183 &self.thread
184 }
185}
186
187impl Sealed for Lua {}
188
189impl RawHandle for Lua {
190 type Raw = RawLuaState;
191
192 fn as_ptr(&self) -> *mut Self::Raw {
193 self.thread.as_ptr()
194 }
195}
196
197impl AsRef<Lua> for Lua {
198 fn as_ref(&self) -> &Lua {
199 self
200 }
201}
202
203impl Drop for Lua {
204 fn drop(&mut self) {
205 unsafe {
206 let thread = self.main_thread();
207 let global = thread.global();
208 if let Some(stack) = thread.stack().value() {
209 thread.close(stack);
210 }
211
212 thread.free_all();
213 UserdataTypeRegistry::free(global.userdata_type_registry_ptr(), thread);
214
215 assert_eq!(
216 global
217 .as_ptr()
218 .as_ref()
219 .unwrap_unchecked()
220 .string_table
221 .n_use,
222 0
223 );
224 if !global
225 .as_ptr()
226 .as_ref()
227 .unwrap_unchecked()
228 .string_table
229 .hash
230 .is_null()
231 {
232 thread.free_array(
233 global
234 .as_ptr()
235 .as_ref()
236 .unwrap_unchecked()
237 .string_table
238 .hash,
239 global
240 .as_ptr()
241 .as_ref()
242 .unwrap_unchecked()
243 .string_table
244 .size
245 .max(0) as usize,
246 0,
247 );
248 }
249
250 thread.free_stack(thread);
251
252 for index in 0..LUA_SIZE_CLASSES {
253 assert!(global.as_ptr().as_ref().unwrap_unchecked().free_pages[index].is_null());
254 assert!(
255 global.as_ptr().as_ref().unwrap_unchecked().free_gco_pages[index].is_null()
256 );
257 }
258
259 assert!(
260 global
261 .as_ptr()
262 .as_ref()
263 .unwrap_unchecked()
264 .all_gco_pages
265 .is_null()
266 );
267 assert_eq!(
268 global.as_ptr().as_ref().unwrap_unchecked().total_bytes,
269 size_of::<RawMainState>()
270 );
271 assert_eq!(
272 global.as_ptr().as_ref().unwrap_unchecked().memcat_bytes[0],
273 size_of::<RawMainState>()
274 );
275 for index in 1..LUA_MEMORY_CATEGORIES {
276 assert_eq!(
277 global.as_ptr().as_ref().unwrap_unchecked().memcat_bytes[index],
278 0
279 );
280 }
281
282 if let Some(close) = global.execution_close() {
283 close(thread);
284 }
285
286 let allocator = Box::from_raw(self.allocator.as_ptr());
287 allocator.deallocate(
288 NonNull::new_unchecked(thread.as_ptr().cast()),
289 Layout::new::<RawMainState>(),
290 );
291 }
292 }
293}