Skip to main content

luau_vm/
lua.rs

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    /// `luaL_newstate`
35    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    /// `lua_mainthread`
182    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}