1use luau_bytecode::opcodes::PROTO_FLAG_USES_EXPORT;
2use luau_common::{ByteSlice, flags};
3
4use super::stack::RawStackAccess;
5use super::{LUA_MULTRET, Thread};
6use crate::call::{CallRuntime, ProtectedCall, ThreadStack, resume_handle, resume_protected};
7use crate::debug::DebugRuntime;
8use crate::function::{FunctionRuntime, RawNativeClosure};
9use crate::gc::{GcBarrier, GcRuntime};
10use crate::handle::RawHandle;
11use crate::native::{
12 NativeCallContext, NativeCallResult, RawNativeContinuation, RawNativeFunction,
13};
14use crate::state::{
15 LUA_CALLINFO_HANDLE, LUA_OK, THREAD_STATUS_BREAK, THREAD_STATUS_OK, THREAD_STATUS_YIELD,
16 ThreadState,
17};
18use crate::string::StringRuntime;
19use crate::value::{TValueCursor, nil_object};
20use crate::{VmControl, VmError, VmErrorResult, VmExit, VmResult};
21
22impl Thread {
24 pub unsafe fn push_native_closure_k(
26 &self,
27 function: RawNativeFunction,
28 debug_name: Option<&'static str>,
29 upvalues: i32,
30 continuation: Option<RawNativeContinuation>,
31 ) -> VmErrorResult {
32 debug_assert!(upvalues >= 0);
33 debug_assert!(
34 upvalues <= unsafe { self.stack_top().offset_from(self.stack_base()) as i32 }
35 );
36 unsafe {
37 self.check_gc()?;
38 self.thread_barrier();
39 self.ensure_stack(self, 1)?;
40
41 let closure = self.new_native_closure(upvalues, Some(self.current_env()))?;
42 let managed_debug_name = if flags::LuauManagedDebugNames.get() {
43 debug_name
44 .map(|name| self.intern_string(name.as_bytes().as_bstr()))
45 .transpose()?
46 } else {
47 None
48 };
49 closure.set_native_data(RawNativeClosure {
50 function: Some(function),
51 continuation,
52 debug_name_deprecated: (!flags::LuauManagedDebugNames.get())
53 .then_some(debug_name)
54 .flatten(),
55 debug_name: managed_debug_name.map_or(core::ptr::null_mut(), |name| name.as_ptr()),
56 });
57 self.set_stack_top(self.stack_top().sub(upvalues as usize));
58
59 for index in (0..upvalues as usize).rev() {
60 closure
61 .native_upvalue(index)
62 .set_obj(self.stack_top().add(index).value_unchecked());
63 }
64
65 let top = self.stack_top();
66 top.value_unchecked().set_closure_value(closure);
67 debug_assert!(top < self.current_call_info().top());
68 self.set_stack_top(top.add(1));
69 }
70 Ok(())
71 }
72
73 pub unsafe fn push_native_function(
75 &self,
76 function: RawNativeFunction,
77 debug_name: Option<&'static str>,
78 ) -> VmErrorResult {
79 unsafe { self.push_native_closure_k(function, debug_name, 0, None) }
80 }
81
82 pub unsafe fn push_native_closure(
84 &self,
85 function: RawNativeFunction,
86 debug_name: Option<&'static str>,
87 upvalues: i32,
88 ) -> VmErrorResult {
89 unsafe { self.push_native_closure_k(function, debug_name, upvalues, None) }
90 }
91
92 pub unsafe fn to_native_function(&self, index: i32) -> Option<RawNativeFunction> {
94 unsafe {
95 let object = self.index_to_addr(index);
96 if object == nil_object() || !object.is_function() {
97 return None;
98 }
99
100 let closure = object.closure_value();
101 if !closure.is_native() {
102 return None;
103 }
104
105 closure.native_data().function
106 }
107 }
108
109 pub unsafe fn clone_function(&self, index: i32) -> VmErrorResult {
111 unsafe {
112 self.check_gc()?;
113 self.thread_barrier();
114 self.ensure_stack(self, 1)?;
115
116 let object = self.to_object(index).unwrap_unchecked();
117 debug_assert!(object.is_function());
118
119 let closure = object.closure_value();
120 let closure_ref = closure.as_ptr().as_ref().unwrap_unchecked();
121 debug_assert!(closure.is_lua());
122
123 let proto = closure.proto().unwrap_unchecked();
124 let environment = self.globals();
125 let new_closure =
126 self.new_lua_closure(i32::from(closure_ref.n_upvalues), Some(environment), proto)?;
127
128 for index in 0..closure_ref.n_upvalues as usize {
129 new_closure
130 .lua_upvalue_ref(index)
131 .set_obj(closure.lua_upvalue_ref(index));
132 }
133
134 let top = self.stack_top();
135 top.value_unchecked().set_closure_value(new_closure);
136 debug_assert!(top < self.current_call_info().top());
137 self.set_stack_top(top.add(1));
138 }
139 Ok(())
140 }
141
142 pub unsafe fn uses_export(&self, index: i32) -> i32 {
144 let object = unsafe { self.index_to_addr(index) };
145 if object == nil_object() || !object.is_function() {
146 return 0;
147 }
148
149 let closure = object.closure_value();
150 if unsafe { closure.is_native() } {
151 return 0;
152 }
153
154 let proto = unsafe { closure.proto().unwrap_unchecked() };
155 i32::from(
156 unsafe { proto.as_ptr().as_ref().unwrap_unchecked().flags } & PROTO_FLAG_USES_EXPORT
157 != 0,
158 )
159 }
160}
161
162fn adjust_results(thread: &Thread, n_results: i32) {
165 if n_results == LUA_MULTRET && unsafe { thread.stack_top() >= thread.current_call_info().top() }
166 {
167 unsafe {
168 thread.current_call_info().set_top(thread.stack_top());
169 }
170 }
171}
172
173fn check_results(thread: &Thread, n_args: i32, n_results: i32) {
175 debug_assert!(
176 n_results == LUA_MULTRET
177 || unsafe {
178 thread
179 .current_call_info()
180 .top()
181 .offset_from(thread.stack_top()) as i32
182 } >= n_results - n_args
183 );
184}
185
186impl Thread {
187 pub unsafe fn call(&self, n_args: i32, n_results: i32) -> VmResult {
189 debug_assert!(n_args >= 0);
190 debug_assert!(n_results >= LUA_MULTRET);
191 debug_assert!(n_args < unsafe { self.stack_top().offset_from(self.stack_base()) as i32 });
192 if n_results > n_args + 1 {
193 unsafe { self.ensure_stack(self, n_results - (n_args + 1))? };
194 }
195 check_results(self, n_args, n_results);
196
197 let result = unsafe {
198 debug_assert!(self.as_ptr().as_ref().unwrap_unchecked().status == THREAD_STATUS_OK);
199 let function = self.stack_top().sub((n_args + 1) as usize);
200 self.call_internal(function, n_results)
201 };
202
203 if matches!(result, Ok(()) | Err(VmExit::Control(_))) {
204 adjust_results(self, n_results);
205 }
206
207 result
208 }
209
210 pub unsafe fn protected_call(&self, n_args: i32, n_results: i32, errfunc: i32) -> VmResult {
212 debug_assert!(n_args >= 0);
213 debug_assert!(n_results >= LUA_MULTRET);
214 #[repr(C)]
215 struct CallContext {
216 function: TValueCursor,
217 n_results: i32,
218 }
219
220 unsafe fn pcall_callback(thread: &Thread, context: &mut CallContext) -> VmResult {
222 unsafe { thread.call_internal(context.function, context.n_results)? };
223 Ok(())
224 }
225
226 if n_results > n_args + 1
227 && let Err(error) = unsafe { self.ensure_stack(self, n_results - (n_args + 1)) }
228 {
229 return Err(error.into());
230 }
231 check_results(self, n_args, n_results);
232
233 unsafe {
234 debug_assert!(n_args < self.stack_top().offset_from(self.stack_base()) as i32);
235 debug_assert!(self.as_ptr().as_ref().unwrap_unchecked().status == THREAD_STATUS_OK);
236
237 let error_function = if errfunc != 0 {
238 let error_function = self.stack_index_to_cursor(errfunc);
239 debug_assert!(error_function.value_unchecked() != nil_object());
240 self.save_stack(error_function)
241 } else {
242 0
243 };
244
245 let function = self.stack_top().sub((n_args + 1) as usize);
246 let mut context = CallContext {
247 function,
248 n_results,
249 };
250
251 let result = self.protected_call_internal(
252 pcall_callback,
253 &mut context,
254 self.save_stack(function),
255 error_function,
256 );
257 adjust_results(self, n_results);
258 result
259 }
260 }
261
262 pub unsafe fn protected_native_call(
264 &self,
265 function: RawNativeFunction,
266 userdata: *mut (),
267 ) -> VmResult {
268 #[repr(C)]
269 struct ProtectedCallContext {
270 function: RawNativeFunction,
271 userdata: *mut (),
272 }
273
274 unsafe fn protected_call_callback(
276 thread: &Thread,
277 context: &mut ProtectedCallContext,
278 ) -> VmResult {
279 unsafe {
280 if thread.check_stack(2) == 0 {
281 return crate::run_error!(thread, "stack limit").map_err(Into::into);
282 }
283
284 thread.push_native_closure_k(context.function, None, 0, None)?;
285 thread.push_light_userdata_tagged(context.userdata, 0)?;
286 let function = thread.stack_top().sub(2);
287 thread.call_internal(function, 0)?;
288 }
289 Ok(())
290 }
291
292 let mut context = ProtectedCallContext { function, userdata };
293 unsafe {
294 debug_assert!(self.as_ptr().as_ref().unwrap_unchecked().status == THREAD_STATUS_OK);
295 self.protected_call_internal(
296 protected_call_callback,
297 &mut context,
298 self.save_stack(self.stack_top()),
299 0,
300 )
301 }
302 }
303
304 pub unsafe fn error<T>(&self) -> VmErrorResult<T> {
306 debug_assert!(unsafe { self.stack_top() > self.stack_base() });
307 Err(VmError::Runtime)
308 }
309
310 #[must_use = "yield_current returns VM control flow that must be propagated by the caller"]
312 pub unsafe fn yield_current(&self, results: i32) -> NativeCallResult {
313 unsafe {
314 debug_assert!(results >= 0);
315 debug_assert!(results <= self.stack_top().offset_from(self.stack_base()) as i32);
316
317 if self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
318 > self
319 .as_ptr()
320 .as_ref()
321 .unwrap_unchecked()
322 .base_native_call_depth
323 {
324 return crate::run_error!(
325 self,
326 "attempt to yield across metamethod/C-call boundary"
327 )
328 .map_err(Into::into);
329 }
330
331 self.set_stack_base(self.stack_top().sub(results as usize));
332 self.as_ptr().as_mut().unwrap_unchecked().status = THREAD_STATUS_YIELD;
333 }
334
335 Err(VmExit::Control(VmControl::Yield))
336 }
337
338 #[must_use = "break_current returns VM control flow that must be propagated by the caller"]
340 pub unsafe fn break_current(&self) -> NativeCallResult {
341 unsafe {
342 if self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
343 > self
344 .as_ptr()
345 .as_ref()
346 .unwrap_unchecked()
347 .base_native_call_depth
348 {
349 return crate::run_error!(
350 self,
351 "attempt to break across metamethod/C-call boundary"
352 )
353 .map_err(Into::into);
354 }
355
356 self.as_ptr().as_mut().unwrap_unchecked().status = THREAD_STATUS_BREAK;
357 }
358
359 Err(VmExit::Control(VmControl::Break))
360 }
361}
362
363impl Thread {
365 pub unsafe fn resume(&self, from: Option<&Thread>, nargs: i32) -> VmResult {
367 unsafe {
368 self.resume_start(from, nargs)?;
369
370 let old_native_call_depth = self.as_ptr().as_ref().unwrap_unchecked().native_call_depth;
371 let mut first_argument = self.stack_top().sub(nargs as usize);
372 let result = self.raw_run_protected(resume_protected, &mut first_argument);
373
374 self.resume_finish(result, old_native_call_depth)
375 }
376 }
377
378 pub unsafe fn resume_error(&self, from: Option<&Thread>) -> VmResult {
380 unsafe {
381 self.resume_start(from, 1)?;
382
383 let old_native_call_depth = self.as_ptr().as_ref().unwrap_unchecked().native_call_depth;
384 let mut result = Err(VmError::Runtime.into());
385
386 if let Some(handler) = self.resume_find_handler() {
387 self.as_ptr().as_mut().unwrap_unchecked().status = VmError::Runtime.status() as u8;
388 let mut handler = handler;
389 result = self.raw_run_protected(resume_handle, &mut handler);
390 }
391
392 self.resume_finish(result, old_native_call_depth)
393 }
394 }
395}
396
397impl Thread {
399 pub unsafe fn call_yieldable(&self, nargs: i32, nresults: i32) -> NativeCallResult {
401 unsafe {
402 let closure = self.current_function();
403 debug_assert!(closure.is_native());
404 debug_assert!(closure.native_data().continuation.is_some());
405
406 self.call(nargs, nresults)?;
407 closure.native_data().continuation.unwrap_unchecked()(
408 NativeCallContext::new(self),
409 LUA_OK,
410 )
411 }
412 }
413
414 pub unsafe fn protected_call_yieldable(
416 &self,
417 nargs: i32,
418 nresults: i32,
419 errfunc: i32,
420 ) -> NativeCallResult {
421 #[repr(C)]
422 struct CallContext {
423 function: TValueCursor,
424 n_results: i32,
425 }
426
427 unsafe fn protected_call_yieldable_run(
428 thread: &Thread,
429 context: &mut CallContext,
430 ) -> VmResult {
431 unsafe {
432 thread.call_int(
433 context.function,
434 context.n_results,
435 thread.is_yieldable() != 0,
436 )?;
437 }
438 Ok(())
439 }
440
441 unsafe {
442 let closure = self.current_function();
443 debug_assert!(closure.is_native());
444 let continuation = closure.native_data().continuation.unwrap_unchecked();
445 debug_assert!(nargs < self.stack_top().offset_from(self.stack_base()) as i32);
446 debug_assert!(errfunc >= 0);
447 debug_assert!(errfunc <= self.stack_top().offset_from(self.stack_base()) as i32);
448
449 let call_info = self.current_call_info();
450 call_info.set_errfunc(errfunc);
451 call_info.as_ptr().as_mut().unwrap_unchecked().flags |= LUA_CALLINFO_HANDLE;
452
453 let function = self.stack_top().sub((nargs + 1) as usize);
454 let mut context = CallContext {
455 function,
456 n_results: nresults,
457 };
458 let saved_function = self.save_stack(function);
459 let saved_error_function = if errfunc != 0 {
460 self.save_stack(self.stack_base().add((errfunc - 1) as usize))
461 } else {
462 0
463 };
464
465 let result = self.protected_call_internal(
466 protected_call_yieldable_run,
467 &mut context,
468 saved_function,
469 saved_error_function,
470 );
471
472 self.expand_stack_limit(self.stack_top());
473
474 let status = match result {
475 Ok(()) => LUA_OK,
476 Err(VmExit::Error(error)) => error.status(),
477 Err(exit) => return Err(exit),
478 };
479
480 call_info.as_ptr().as_mut().unwrap_unchecked().flags &= !LUA_CALLINFO_HANDLE;
481
482 continuation(NativeCallContext::new(self), status)
483 }
484 }
485}