1use crate::debug::DebugRuntime;
2use crate::handle::RawHandle;
3use crate::handle::sealed::Sealed;
4use crate::memory::MemoryRuntime;
5use crate::state::ThreadState;
6use crate::state::{CallInfoCursor, RawCallInfo};
7use crate::thread::{LUAI_MAX_CALLS, LUAI_MAX_NATIVE_CALLS, Thread};
8use crate::value::{RawTValue, TValueCursor};
9use crate::{VmError, VmErrorResult};
10
11pub(super) const MAX_STACK_SIZE: i32 =
12 ((1024 / core::mem::size_of::<RawTValue>()) * 1024 * 1024) as i32;
13pub(super) const MAX_CALLS_HARD: usize = LUAI_MAX_CALLS + (LUAI_MAX_CALLS >> 3);
14
15#[allow(
23 clippy::missing_safety_doc,
24 reason = "all methods share the capability-level safety contract"
25)]
26pub trait ThreadStack: Sealed {
27 unsafe fn check_stack_internal(&self, needed: i32) -> VmErrorResult;
28 unsafe fn check_stack_for_new_ci(&self, needed: i32) -> VmErrorResult;
29 unsafe fn get_grown_stack_size(&self, needed: i32) -> i32;
30 unsafe fn stack_limit_reached(&self, needed: i32) -> bool;
31 unsafe fn save_stack(&self, pointer: TValueCursor) -> isize;
32 unsafe fn restore_stack(&self, offset: isize) -> TValueCursor;
33 unsafe fn save_ci(&self, pointer: CallInfoCursor) -> isize;
34 unsafe fn restore_ci(&self, offset: isize) -> CallInfoCursor;
35 unsafe fn expand_stack_limit(&self, pointer: TValueCursor);
36 unsafe fn realloc_stack(&self, new_size: i32, for_new_ci: bool) -> VmErrorResult;
37 unsafe fn realloc_ci(&self, new_size: i32) -> VmErrorResult;
38 unsafe fn grow_stack(&self, needed: i32) -> VmErrorResult;
39 unsafe fn grow_ci(&self) -> VmErrorResult<CallInfoCursor>;
40 unsafe fn incr_ci(&self) -> VmErrorResult<CallInfoCursor>;
41 unsafe fn check_c_stack(&self) -> VmErrorResult;
42}
43
44impl Thread {
45 unsafe fn correct_stack(&self, old_stack: TValueCursor) {
47 unsafe {
48 let new_stack_cursor = self.stack();
49 let top_offset = self.stack_top().addr_offset_from(old_stack) as usize;
50 self.set_stack_top(new_stack_cursor.add(top_offset));
51
52 let mut upvalue = self.open_upvalue();
53 while let Some(current_upvalue) = upvalue {
54 current_upvalue.rebase_value(old_stack, new_stack_cursor);
55 upvalue = current_upvalue.open_data().thread_next();
56 }
57
58 let mut call_info_cursor = self.base_call_info_cursor();
59 let current_call_info_cursor = self.current_call_info_cursor();
60 while call_info_cursor <= current_call_info_cursor {
61 call_info_cursor
62 .call_info_unchecked()
63 .rebase_stack(old_stack, new_stack_cursor);
64 call_info_cursor = call_info_cursor.add(1);
65 }
66
67 let base_offset = self.stack_base().addr_offset_from(old_stack) as usize;
68 self.set_stack_base(new_stack_cursor.add(base_offset));
69 }
70 }
71}
72
73impl ThreadStack for Thread {
74 unsafe fn check_stack_internal(&self, needed: i32) -> VmErrorResult {
76 if unsafe { self.stack_limit_reached(needed) } {
77 unsafe { self.grow_stack(needed)? };
78 }
79 Ok(())
80 }
81
82 unsafe fn check_stack_for_new_ci(&self, needed: i32) -> VmErrorResult {
84 if unsafe { self.stack_limit_reached(needed) } {
85 unsafe { self.realloc_stack(self.get_grown_stack_size(needed), true)? };
86 }
87 Ok(())
88 }
89
90 unsafe fn get_grown_stack_size(&self, needed: i32) -> i32 {
92 let stack_size = unsafe { self.as_ptr().as_ref().unwrap_unchecked().stack_size };
93 if needed <= stack_size {
94 2 * stack_size
95 } else {
96 stack_size + needed
97 }
98 }
99
100 unsafe fn stack_limit_reached(&self, needed: i32) -> bool {
102 let thread = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
103 let available_bytes = (thread.stack_last as usize).wrapping_sub(thread.top as usize);
104 available_bytes <= needed as usize * core::mem::size_of::<RawTValue>()
105 }
106
107 unsafe fn save_stack(&self, pointer: TValueCursor) -> isize {
109 let stack = unsafe { self.stack() };
110 unsafe {
111 pointer
112 .as_ptr()
113 .cast::<u8>()
114 .offset_from(stack.as_ptr().cast::<u8>())
115 }
116 }
117
118 unsafe fn restore_stack(&self, offset: isize) -> TValueCursor {
120 unsafe { TValueCursor::from_ptr(self.stack().as_ptr().cast::<u8>().offset(offset).cast()) }
121 }
122
123 unsafe fn save_ci(&self, pointer: CallInfoCursor) -> isize {
125 let base_ci = unsafe { self.as_ptr().as_ref().unwrap_unchecked().base_ci };
126 unsafe {
127 pointer
128 .as_ptr()
129 .cast::<u8>()
130 .offset_from(base_ci.cast::<u8>())
131 }
132 }
133
134 unsafe fn restore_ci(&self, offset: isize) -> CallInfoCursor {
136 unsafe {
137 CallInfoCursor::from_ptr(
138 self.as_ptr()
139 .as_ref()
140 .unwrap_unchecked()
141 .base_ci
142 .cast::<u8>()
143 .offset(offset)
144 .cast::<RawCallInfo>(),
145 )
146 }
147 }
148
149 unsafe fn expand_stack_limit(&self, pointer: TValueCursor) {
151 debug_assert!(pointer <= unsafe { self.stack_last() });
152
153 unsafe {
154 let current_call_info = self.current_call_info();
155 if current_call_info.top() < pointer {
156 current_call_info.set_top(pointer);
157 }
158 }
159 }
160
161 unsafe fn realloc_stack(&self, new_size: i32, for_new_ci: bool) -> VmErrorResult {
163 unsafe {
164 if new_size > MAX_STACK_SIZE {
165 if for_new_ci {
166 let parent_cursor = self.current_call_info_cursor().sub(1);
167 self.restore_call_frame(
168 parent_cursor,
169 parent_cursor.call_info_unchecked().top(),
170 );
171 }
172
173 return Err(VmError::Memory);
174 }
175
176 let old_stack = self.stack();
177 let real_size = new_size + crate::state::EXTRA_STACK as i32;
178 debug_assert!(
179 self.as_ptr()
180 .as_ref()
181 .unwrap_unchecked()
182 .stack_last
183 .offset_from(self.as_ptr().as_ref().unwrap_unchecked().stack)
184 == self.as_ptr().as_ref().unwrap_unchecked().stack_size as isize
185 - crate::state::EXTRA_STACK as isize
186 );
187
188 let old_stack_size = self.as_ptr().as_ref().unwrap_unchecked().stack_size as usize;
189 let new_stack_size = real_size as usize;
190 let memcat = self.as_ptr().as_ref().unwrap_unchecked().active_memcat;
191 let new_stack = TValueCursor::from_ptr(self.realloc_array(
192 old_stack.as_ptr(),
193 old_stack_size,
194 new_stack_size,
195 memcat,
196 )?);
197
198 self.set_stack(new_stack);
199
200 for index in old_stack_size..new_stack_size {
201 new_stack.add(index).value_unchecked().set_nil();
202 }
203
204 let raw = self.as_ptr().as_mut().unwrap_unchecked();
205 raw.stack_size = real_size;
206 raw.stack_last = new_stack.add(new_size as usize).as_ptr();
207 self.correct_stack(old_stack);
208 }
209 Ok(())
210 }
211
212 unsafe fn realloc_ci(&self, new_size: i32) -> VmErrorResult {
214 unsafe {
215 let old_ci = self.as_ptr().as_ref().unwrap_unchecked().base_ci;
216 let old_size = self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize;
217 let new_size_usize = new_size as usize;
218 let memcat = self.as_ptr().as_ref().unwrap_unchecked().active_memcat;
219 let new_ci = self.realloc_array(old_ci, old_size, new_size_usize, memcat)?;
220 let current_offset =
221 self.current_call_info_cursor()
222 .offset_from(self.base_call_info_cursor()) as usize;
223
224 let raw = self.as_ptr().as_mut().unwrap_unchecked();
225 raw.base_ci = new_ci;
226 raw.size_ci = new_size;
227 raw.end_ci = new_ci.add(new_size_usize - 1);
228 self.set_current_call_info(self.base_call_info_cursor().add(current_offset));
229 }
230 Ok(())
231 }
232
233 unsafe fn grow_stack(&self, needed: i32) -> VmErrorResult {
235 unsafe { self.realloc_stack(self.get_grown_stack_size(needed), false) }
236 }
237
238 unsafe fn grow_ci(&self) -> VmErrorResult<CallInfoCursor> {
240 unsafe {
241 if self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize >= MAX_CALLS_HARD {
242 return Err(VmError::ErrorHandler);
243 }
244
245 let current_size = self.as_ptr().as_ref().unwrap_unchecked().size_ci;
246 let request = current_size * 2;
247 let new_size = if current_size as usize >= LUAI_MAX_CALLS {
248 MAX_CALLS_HARD as i32
249 } else if request as usize >= LUAI_MAX_CALLS {
250 LUAI_MAX_CALLS as i32
251 } else {
252 request
253 };
254
255 self.realloc_ci(new_size)?;
256
257 if self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize > LUAI_MAX_CALLS {
258 return crate::run_error!(self, "stack overflow");
259 }
260
261 let call_info = self.current_call_info_cursor().add(1);
262 self.set_current_call_info(call_info);
263 Ok(call_info)
264 }
265 }
266
267 unsafe fn incr_ci(&self) -> VmErrorResult<CallInfoCursor> {
269 unsafe {
270 if self.current_call_info_cursor()
271 == self
272 .base_call_info_cursor()
273 .add(self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize - 1)
274 {
275 self.grow_ci()
276 } else {
277 let call_info = self.current_call_info_cursor().add(1);
278 self.set_current_call_info(call_info);
279 Ok(call_info)
280 }
281 }
282 }
283
284 unsafe fn check_c_stack(&self) -> VmErrorResult {
286 unsafe {
287 let native_call_depth = self.as_ptr().as_ref().unwrap_unchecked().native_call_depth;
288 if native_call_depth == LUAI_MAX_NATIVE_CALLS {
289 return crate::run_error!(self, "C stack overflow");
290 } else if native_call_depth >= crate::state::MAX_NATIVE_CALLS_HARD {
291 return Err(VmError::ErrorHandler);
292 }
293 }
294 Ok(())
295 }
296}