Skip to main content

luau_vm/state/
lifecycle.rs

1use core::mem::size_of;
2use core::ptr::{self, NonNull};
3
4use luau_common::{ByteSlice, flags};
5
6use crate::gc::GcObject;
7use crate::handle::RawHandle;
8use crate::handle::sealed::Sealed;
9use crate::memory::{LuaPage, MemoryRuntime};
10use crate::metamethod::{MetamethodRuntime, TmEvent};
11use crate::string::{LUA_MIN_STRING_TABLE_SIZE, StringRuntime};
12use crate::table::TableRuntime;
13use crate::thread::{LUA_MIN_STACK, Thread};
14use crate::types::LUA_TTHREAD;
15use crate::value::{RawTValue, TValue, TValueCursor};
16use crate::{VmErrorResult, VmResult};
17
18use super::{
19    BASIC_CI_SIZE, EXTRA_STACK, GlobalState, INITIAL_STACK_SIZE, LUA_ERRERRMSG, LUA_MEMERRMSG,
20    RawCallInfo, RawLuaState, THREAD_STATUS_OK, ThreadState,
21};
22
23/// Unstable thread allocation and lifecycle capability.
24///
25/// # Safety
26///
27/// Thread, page, and parent handles must be live and belong to the same VM.
28/// Initialization and destruction must occur exactly once in the required
29/// order, and no outstanding handle may be used after a state is freed.
30#[allow(
31    clippy::missing_safety_doc,
32    reason = "all methods share the capability-level safety contract"
33)]
34pub trait ThreadLifecycle: Sealed {
35    /// thread-owned allocation size
36    unsafe fn allocation_size(&self) -> usize;
37
38    /// `luaE_newthread`
39    unsafe fn new_thread_internal(&self) -> VmErrorResult<Thread>;
40
41    /// `luaE_freethread`
42    unsafe fn free_thread(&self, thread: &Thread, page: LuaPage);
43}
44impl ThreadLifecycle for Thread {
45    unsafe fn allocation_size(&self) -> usize {
46        unsafe {
47            size_of::<RawLuaState>()
48                + size_of::<RawTValue>()
49                    * self.as_ptr().as_ref().unwrap_unchecked().stack_size as usize
50                + size_of::<RawCallInfo>()
51                    * self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize
52        }
53    }
54
55    /// `luaE_newthread`
56    unsafe fn new_thread_internal(&self) -> VmErrorResult<Thread> {
57        unsafe {
58            let global = self.global();
59            let memcat = self.as_ptr().as_ref().unwrap_unchecked().active_memcat;
60            let thread = self.new_gco::<Thread>(size_of::<RawLuaState>(), memcat)?;
61            GcObject::from(&thread).init_header(self, LUA_TTHREAD as u8);
62
63            thread.preinit_state(global);
64            thread.as_ptr().as_mut().unwrap_unchecked().active_memcat = memcat;
65            self.init_stack(&thread)?;
66
67            let source = self.as_ptr().as_ref().unwrap_unchecked();
68            thread.set_globals(self.globals());
69            thread.as_ptr().as_mut().unwrap_unchecked().single_step = source.single_step;
70
71            debug_assert!(GcObject::from(&thread).is_white());
72            Ok(thread)
73        }
74    }
75
76    /// `luaE_freethread`
77    unsafe fn free_thread(&self, thread: &Thread, page: LuaPage) {
78        unsafe {
79            if let Some(user_thread) = self.global().user_thread_callback() {
80                user_thread(None, thread);
81            }
82
83            self.free_stack(thread);
84            self.free_gco(
85                thread.into(),
86                size_of::<RawLuaState>(),
87                thread.as_ptr().as_ref().unwrap_unchecked().memcat,
88                page,
89            );
90        }
91    }
92}
93
94impl Thread {
95    /// `stack_init`
96    pub(crate) unsafe fn init_stack(&self, thread: &Thread) -> VmErrorResult {
97        unsafe {
98            let memcat = thread.as_ptr().as_ref().unwrap_unchecked().active_memcat;
99            let base_ci = self.new_array::<RawCallInfo>(BASIC_CI_SIZE, memcat)?;
100            {
101                let raw = thread.as_ptr().as_mut().unwrap_unchecked();
102                raw.base_ci = base_ci;
103                raw.size_ci = BASIC_CI_SIZE as i32;
104                raw.end_ci = base_ci.add(BASIC_CI_SIZE - 1);
105            }
106
107            let stack = self.new_array::<RawTValue>(INITIAL_STACK_SIZE, memcat)?;
108            {
109                let raw = thread.as_ptr().as_mut().unwrap_unchecked();
110                raw.stack = stack;
111                raw.stack_size = INITIAL_STACK_SIZE as i32;
112                raw.stack_last = stack.add(INITIAL_STACK_SIZE - EXTRA_STACK);
113            }
114
115            for index in 0..INITIAL_STACK_SIZE {
116                TValue::from_raw(NonNull::new_unchecked(stack.add(index))).set_nil();
117            }
118
119            thread.set_current_call_info(thread.base_call_info_cursor());
120
121            let function = TValueCursor::from_ptr(stack);
122            function.value_unchecked().set_nil();
123            let base = function.add(1);
124            thread
125                .base_call_info()
126                .init_call(function, base.add(LUA_MIN_STACK), 0, None);
127            thread.set_stack_top(base);
128            thread.set_stack_base(base);
129        }
130        Ok(())
131    }
132
133    /// `freestack`
134    pub(crate) unsafe fn free_stack(&self, thread: &Thread) {
135        unsafe {
136            let memcat = thread.as_ptr().as_ref().unwrap_unchecked().active_memcat;
137            self.free_array(
138                thread.as_ptr().as_ref().unwrap_unchecked().base_ci,
139                thread.as_ptr().as_ref().unwrap_unchecked().size_ci as usize,
140                memcat,
141            );
142            self.free_array(
143                thread.as_ptr().as_ref().unwrap_unchecked().stack,
144                thread.as_ptr().as_ref().unwrap_unchecked().stack_size as usize,
145                memcat,
146            );
147        }
148    }
149
150    /// `preinit_state`
151    pub(crate) unsafe fn preinit_state(&self, global: GlobalState) {
152        let raw = unsafe { self.as_ptr().as_mut().unwrap_unchecked() };
153        raw.global = global.as_ptr();
154        raw.stack = ptr::null_mut();
155        raw.stack_size = 0;
156        raw.gt = ptr::null_mut();
157        raw.open_upval = ptr::null_mut();
158        raw.size_ci = 0;
159        raw.native_call_depth = 0;
160        raw.base_native_call_depth = 0;
161        raw.status = THREAD_STATUS_OK;
162        raw.base_ci = ptr::null_mut();
163        raw.ci = ptr::null_mut();
164        raw.name_call = ptr::null_mut();
165        raw.cached_slot = 0;
166        raw.single_step = false;
167        raw.is_active = false;
168        raw.active_memcat = 0;
169        raw.userdata = ptr::null_mut();
170        raw.top = ptr::null_mut();
171        raw.base = ptr::null_mut();
172        raw.stack_last = ptr::null_mut();
173        raw.end_ci = ptr::null_mut();
174        raw.gc_list = ptr::null_mut();
175    }
176
177    /// `f_luaopen`
178    pub(crate) unsafe fn open_main_state(&self) -> VmErrorResult {
179        unsafe {
180            self.init_stack(self)?;
181
182            let globals = self.new_table_internal(0, 2)?;
183            let registry = self.new_table_internal(0, 2)?;
184
185            self.set_globals(globals);
186            self.global().registry().set_table_value(registry);
187
188            self.resize(LUA_MIN_STRING_TABLE_SIZE as i32)?;
189            self.init()?;
190
191            if flags::LuauGcTraceUdata.get() {
192                let weak_registry = self.new_table_internal(0, 0)?;
193                let metatable = self.new_table_internal(0, 1)?;
194                let mode = self.intern_string(b"v".as_bstr())?;
195                let mode_slot = self
196                    .set_str(metatable, self.global().tm_name(TmEvent::Mode as usize))?
197                    .node_unchecked()
198                    .value_unchecked();
199                mode_slot.set_string_value(mode);
200                weak_registry.set_metatable(Some(metatable));
201                self.global().weak_registry().set_table_value(weak_registry);
202            }
203
204            self.intern_string(LUA_MEMERRMSG.as_bstr())?.fix();
205            self.intern_string(LUA_ERRERRMSG.as_bstr())?.fix();
206
207            let global = self.global();
208            let global_ref = global.as_ptr().as_mut().unwrap_unchecked();
209            global_ref.gc_threshold = 4 * global_ref.total_bytes;
210        }
211        Ok(())
212    }
213}
214
215/// `f_luaopen`
216pub(crate) unsafe fn open_main_state(thread: &Thread, _: &mut ()) -> VmResult {
217    unsafe { thread.open_main_state() }?;
218    Ok(())
219}