1#[cfg(feature = "compiler")]
2pub mod compile;
3
4pub mod ffi;
5mod libs;
6mod memory;
7mod threads;
8mod userdata;
9
10use std::{
11 any::Any,
12 cell::Cell,
13 ffi::{c_int, c_uint, c_void, CStr, CString},
14 ptr::{null, null_mut},
15 rc::Rc,
16 slice,
17};
18
19use ffi::{
20 luauconf::{LUAI_MAXCSTACK, LUA_MEMORY_CATEGORIES},
21 prelude::*,
22};
23use memory::{luau_alloc_cb, DefaultLuauAllocator};
24use userdata::{
25 drop_userdata, dtor_rs_luau_userdata_callback, Userdata, UserdataBorrowError, UserdataRef,
26 UserdataRefMut, UD_TAG,
27};
28
29pub use ffi::prelude::LuauStatus;
30pub use libs::LuauLibs;
31pub use memory::LuauAllocator;
32pub use threads::LuauThread;
33
34macro_rules! luau_stack_precondition {
35 ($cond:expr) => {
36 assert!(
37 $cond,
38 "Stack indicies should not exceed the top of the stack or extend below."
39 )
40 };
41}
42
43struct AssociatedData {
44 main_thread_rc: Rc<Cell<bool>>,
45 allocator: Box<dyn LuauAllocator>,
46 app_data: Option<Box<dyn Any>>,
47}
48
49#[cfg(feature = "codegen")]
50pub fn codegen_supported() -> bool {
52 unsafe { luau_codegen_supported() == 1 }
53}
54
55pub struct Luau {
57 owned: bool,
58 state: *mut _LuaState,
59}
60
61impl Luau {
62 unsafe fn new_state(allocator: impl LuauAllocator + 'static) -> *mut _LuaState {
63 let associated_data = Box::new(AssociatedData {
64 main_thread_rc: Rc::new(Cell::new(true)),
65 app_data: None,
66 allocator: Box::new(allocator),
67 });
68
69 let state = lua_newstate(luau_alloc_cb, Box::into_raw(associated_data) as _);
70
71 lua_setuserdatadtor(state, UD_TAG, Some(dtor_rs_luau_userdata_callback));
72
73 (*lua_callbacks(state)).panic = Some(fatal_error_handler);
74
75 state
76 }
77
78 pub fn new(allocator: impl LuauAllocator + 'static) -> Self {
79 let state = unsafe { Self::new_state(allocator) };
80
81 if state.is_null() {
82 panic!("Initialization of Luau failed");
83 }
84
85 Self { owned: true, state }
86 }
87
88 #[cfg(feature = "codegen")]
89 pub fn enable_codegen(&self) {
91 unsafe {
92 luau_codegen_create(self.state);
93 }
94 }
95
96 pub unsafe fn from_ptr(state: *mut _LuaState) -> Self {
101 Self {
102 owned: false,
103 state,
104 }
105 }
106
107 pub unsafe fn from_ptr_owned(state: *mut _LuaState) -> Self {
112 Self { owned: true, state }
113 }
114
115 const ASSOCIATED_DATA_ERROR: &str = "Expected associated data structure";
116
117 pub(crate) fn get_associated(&self) -> &AssociatedData {
118 unsafe {
119 let mut ptr: *const AssociatedData = null();
120 lua_getallocf(self.state, &raw mut ptr as _);
121
122 ptr.as_ref().expect(Self::ASSOCIATED_DATA_ERROR)
123 }
124 }
125
126 pub(crate) fn get_associated_mut(&self) -> *mut AssociatedData {
127 unsafe {
128 let mut ptr: *mut AssociatedData = null_mut();
129 lua_getallocf(self.state, &raw mut ptr as _);
130
131 assert!(!ptr.is_null(), "{}", Self::ASSOCIATED_DATA_ERROR);
132
133 ptr
134 }
135 }
136
137 pub fn get_app_data<T: Any>(&self) -> Option<&T> {
138 self.get_associated()
139 .app_data
140 .as_ref()
141 .and_then(|v| v.downcast_ref())
142 }
143
144 pub fn set_app_data<T: Any>(&self, ud: Option<T>) -> Option<Box<dyn Any>> {
146 let associated = unsafe { &mut *self.get_associated_mut() };
147
148 if let Some(v) = ud {
149 let boxed_data = Box::new(v);
150
151 associated.app_data.replace(boxed_data)
152 } else {
153 associated.app_data.take()
154 }
155 }
156
157 pub fn load_libs(&self, lib: LuauLibs) {
158 macro_rules! load_lib {
159 ($func:ident) => {
160 unsafe {
161 self.push_raw_function(
162 $func,
163 Some(&CString::new(stringify!($func)).unwrap()),
164 0,
165 None,
166 );
167 self.push_string("");
168 self.call(1, 0);
169 };
170 };
171
172 ($idnt:expr, $func:ident) => {
173 unsafe {
174 self.push_raw_function(
175 $func,
176 Some(&CString::new(stringify!($func)).unwrap()),
177 0,
178 None,
179 );
180 self.push_string($idnt);
181 self.call(1, 0);
182 };
183 };
184 }
185
186 if lib.has(LuauLibs::ALL_LIBS) {
187 unsafe { luaL_openlibs(self.state) };
188
189 return;
190 }
191
192 if lib.has(LuauLibs::LIB_BASE) {
193 load_lib!(luaopen_base);
194 }
195
196 if lib.has(LuauLibs::LIB_COROUTINE) {
197 load_lib!(LUA_COLIBNAME, luaopen_coroutine);
198 }
199
200 if lib.has(LuauLibs::LIB_TABLE) {
201 load_lib!(LUA_TABLIBNAME, luaopen_table);
202 }
203
204 if lib.has(LuauLibs::LIB_OS) {
205 load_lib!(LUA_OSLIBNAME, luaopen_os);
206 }
207
208 if lib.has(LuauLibs::LIB_STRING) {
209 load_lib!(LUA_STRLIBNAME, luaopen_string);
210 }
211
212 if lib.has(LuauLibs::LIB_MATH) {
213 load_lib!(LUA_MATHLIBNAME, luaopen_math);
214 }
215
216 if lib.has(LuauLibs::LIB_DEBUG) {
217 load_lib!(LUA_DBLIBNAME, luaopen_debug);
218 }
219
220 if lib.has(LuauLibs::LIB_UTF8) {
221 load_lib!(LUA_UTF8LIBNAME, luaopen_utf8);
222 }
223
224 if lib.has(LuauLibs::LIB_BIT32) {
225 load_lib!(LUA_BITLIBNAME, luaopen_bit32);
226 }
227
228 if lib.has(LuauLibs::LIB_BUFFER) {
229 load_lib!(LUA_BUFFERLIBNAME, luaopen_buffer);
230 }
231 }
232
233 #[inline]
234 pub fn to_ptr(&self) -> *mut _LuaState {
235 self.state
236 }
237
238 #[inline]
239 pub fn top(&self) -> c_int {
240 unsafe { lua_gettop(self.state) }
241 }
242
243 pub fn status(&self) -> LuauStatus {
245 unsafe { lua_status(self.state) }
246 }
247
248 pub fn yield_luau(&self, nresults: c_int) -> c_int {
252 assert!(
253 self.top() >= nresults,
254 "The number of yield returns must not exceed the stack size"
255 );
256
257 unsafe { lua_yield(self.state, nresults) }
258 }
259
260 pub fn break_luau(&self) -> c_int {
264 unsafe { lua_break(self.state) }
265 }
266
267 pub fn error(&self) -> c_int {
269 luau_stack_precondition!(self.check_index(-1));
270
271 unsafe { lua_error(self.state) }
273 }
274
275 pub fn type_of(&self, idx: c_int) -> LuauType {
277 luau_stack_precondition!(self.check_index(idx));
278
279 unsafe { lua_type(self.state, idx) }
280 }
281
282 pub fn pop(&self, n: c_int) {
284 luau_stack_precondition!(self.check_index(-n));
286
287 unsafe { lua_settop(self.state, -(n + 1)) }
289 }
290
291 pub fn upvalue(&self, uv_idx: c_int) -> c_int {
293 lua_upvalueindex(uv_idx)
294 }
295
296 pub fn set_memory_category(&self, cat: c_int) {
298 assert!(
299 cat < LUA_MEMORY_CATEGORIES,
300 "Memory category index must not exceed {LUA_MEMORY_CATEGORIES}"
301 );
302
303 unsafe {
304 lua_setmemcat(self.state, cat);
305 }
306 }
307
308 pub fn check_index(&self, idx: c_int) -> bool {
309 if idx <= LUA_REGISTRYINDEX {
310 return true;
311 }
312
313 if idx == 0 {
314 return false;
315 }
316
317 let top = self.top();
318
319 let idx = if idx < 0 {
320 top.wrapping_add(idx)
322 } else {
323 idx
324 };
325
326 if idx < LUA_GLOBALSINDEX {
327 return true;
329 }
330
331 idx >= 0 && idx <= top && idx < LUAI_MAXCSTACK }
337
338 pub fn check_stack(&self, sz: c_int) -> bool {
339 unsafe { lua_checkstack(self.state, sz) == 1 }
340 }
341
342 #[inline]
343 pub fn registry(&self) -> c_int {
344 LUA_REGISTRYINDEX
345 }
346
347 #[inline]
348 pub fn globals(&self) -> c_int {
349 LUA_GLOBALSINDEX
350 }
351
352 pub fn check_args(&self, count: c_int, extra_message: Option<&CStr>) {
353 if self.top() >= count {
354 return;
355 }
356
357 unsafe {
358 luaL_argerrorL(
359 self.state,
360 count - self.top(),
361 extra_message.map(CStr::as_ptr).unwrap_or(null()),
362 );
363 }
364 }
365
366 pub fn is_nil(&self, idx: c_int) -> bool {
368 self.type_of(idx) == LuauType::LUA_TNIL
369 }
370
371 pub fn push_nil(&self) {
373 luau_stack_precondition!(self.check_stack(1));
374
375 unsafe {
377 lua_pushnil(self.state);
378 }
379 }
380
381 pub fn is_boolean(&self, idx: c_int) -> bool {
383 self.type_of(idx) == LuauType::LUA_TBOOLEAN
384 }
385
386 pub fn to_boolean(&self, idx: c_int) -> bool {
388 luau_stack_precondition!(self.check_index(idx));
389
390 unsafe { lua_toboolean(self.state, idx) == 1 }
392 }
393
394 pub fn push_boolean(&self, value: bool) {
396 luau_stack_precondition!(self.check_stack(1));
397
398 unsafe {
400 lua_pushboolean(self.state, value as i32);
401 }
402 }
403
404 pub fn is_number(&self, idx: c_int) -> bool {
406 self.type_of(idx) == LuauType::LUA_TNUMBER
407 }
408
409 pub fn push_integer(&self, n: c_int) {
411 luau_stack_precondition!(self.check_stack(1));
412
413 unsafe {
415 lua_pushinteger(self.state, n);
416 }
417 }
418
419 pub fn push_unsigned_integer(&self, n: c_uint) {
421 luau_stack_precondition!(self.check_stack(1));
422
423 unsafe {
425 lua_pushunsigned(self.state, n);
426 }
427 }
428
429 pub fn push_number(&self, n: f64) {
431 luau_stack_precondition!(self.check_stack(1));
433
434 unsafe {
436 lua_pushnumber(self.state, n);
437 }
438 }
439
440 pub fn to_number(&self, idx: c_int) -> Option<f64> {
444 luau_stack_precondition!(self.check_index(idx));
445
446 let mut is_number = 0;
447 let number = unsafe { lua_tonumberx(self.state, idx, &raw mut is_number) };
449
450 if is_number == 1 {
451 Some(number)
452 } else {
453 None
454 }
455 }
456
457 pub fn is_string(&self, idx: c_int) -> bool {
459 self.type_of(idx) == LuauType::LUA_TSTRING
460 }
461
462 pub fn push_string(&self, str: impl AsRef<[u8]>) {
464 luau_stack_precondition!(self.check_stack(1));
465
466 let slice = str.as_ref();
467
468 unsafe {
470 lua_pushlstring(self.state, slice.as_ptr() as _, slice.len());
471 }
472 }
473
474 pub fn to_str_slice(&self, idx: c_int) -> Option<&[u8]> {
476 luau_stack_precondition!(self.check_index(idx));
477
478 let mut len = 0;
480 let data = unsafe { lua_tolstring(self.state, idx, &mut len) };
482
483 if !data.is_null() {
484 unsafe { Some(std::slice::from_raw_parts(data as _, len)) }
486 } else {
487 None
488 }
489 }
490
491 pub fn to_str(&self, idx: c_int) -> Option<Result<&str, std::str::Utf8Error>> {
493 self.to_str_slice(idx).map(|v| std::str::from_utf8(v))
495 }
496
497 pub fn convert_to_str_slice(&self, idx: c_int) -> &[u8] {
499 luau_stack_precondition!(self.check_index(idx));
500
501 unsafe {
502 let mut len = 0;
503 let data = luaL_tolstring(self.state, idx, &raw mut len);
504
505 if data.is_null() {
506 unreachable!("Luau string conversion returned NULL ptr");
507 } else {
508 std::slice::from_raw_parts(data as _, len)
509 }
510 }
511 }
512
513 pub fn is_userdata<T: Any>(&self, idx: c_int) -> bool {
515 luau_stack_precondition!(self.check_index(idx));
516
517 unsafe {
519 let userdata_ptr: *mut Userdata<()> =
520 lua_touserdatatagged(self.state, idx, UD_TAG) as _;
521
522 !userdata_ptr.is_null() && (*userdata_ptr).is::<T>()
523 }
524 }
525
526 pub fn is_any_userdata<T: Any>(&self, idx: c_int) -> bool {
528 luau_stack_precondition!(self.check_index(idx));
529
530 unsafe { lua_isuserdata(self.state, idx) == 1 }
532 }
533
534 pub fn push_userdata<T: Any>(&self, object: T) {
536 luau_stack_precondition!(self.check_stack(1));
537
538 unsafe {
544 let userdata_ptr: *mut Userdata<T> =
545 lua_newuserdatatagged(self.state, size_of::<Userdata<T>>(), UD_TAG).cast();
546
547 let dtor = if std::mem::needs_drop::<T>() {
548 let fn_item: unsafe fn(*mut Userdata<T>) = drop_userdata::<T>;
549
550 Some(fn_item)
551 } else {
552 None
553 };
554
555 userdata_ptr.write(Userdata {
556 id: object.type_id(),
557 count_cell: Cell::new(0),
558 dtor,
559 inner: object,
560 });
561 }
562 }
563
564 fn get_userdata_ptr<T: Any>(&self, idx: c_int) -> Option<*mut Userdata<T>> {
565 luau_stack_precondition!(self.check_index(idx));
566
567 unsafe {
569 let userdata_ptr: *mut Userdata<()> =
570 lua_touserdatatagged(self.state, idx, UD_TAG) as _;
571
572 if !userdata_ptr.is_null() && (*userdata_ptr).is::<T>() {
573 Some(userdata_ptr as _)
574 } else {
575 None
576 }
577 }
578 }
579
580 pub fn try_borrow_userdata<T: Any>(
584 &self,
585 idx: c_int,
586 ) -> Option<Result<UserdataRef<T>, UserdataBorrowError>> {
587 unsafe {
589 let userdata_ptr = self.get_userdata_ptr(idx)?;
590
591 Some(UserdataRef::try_from_ptr(userdata_ptr))
592 }
593 }
594
595 pub fn borrow_userdata<T: Any>(&self, idx: c_int) -> Option<UserdataRef<T>> {
599 self.try_borrow_userdata(idx).map(Result::unwrap)
600 }
601
602 pub fn try_borrow_userdata_mut<T: Any>(
606 &self,
607 idx: c_int,
608 ) -> Option<Result<UserdataRefMut<T>, UserdataBorrowError>> {
609 unsafe {
611 let userdata_ptr = self.get_userdata_ptr(idx)?;
612
613 Some(UserdataRefMut::try_from_ptr(userdata_ptr))
614 }
615 }
616
617 pub unsafe fn get_userdata_unchecked<T: 'static>(&self, idx: c_int) -> Option<&mut T> {
624 luau_stack_precondition!(self.check_index(idx));
625
626 unsafe { Some(&mut (*self.get_userdata_ptr(idx)?).inner) }
628 }
629
630 pub fn is_lightuserdata(&self, idx: c_int) -> bool {
632 self.type_of(idx) == LuauType::LUA_TLIGHTUSERDATA
633 }
634
635 pub fn to_lightuserdata<T>(&self, idx: c_int) -> Option<*mut T> {
637 luau_stack_precondition!(self.check_index(idx));
638
639 unsafe {
641 let ptr: *mut T = lua_tolightuserdata(self.state, idx).cast();
642
643 if ptr.is_null() {
644 None
645 } else {
646 Some(ptr)
647 }
648 }
649 }
650
651 pub fn is_buffer(&self, idx: c_int) -> bool {
653 self.type_of(idx) == LuauType::LUA_TBUFFER
654 }
655
656 pub fn push_buffer(&mut self, size: usize) -> &mut [u8] {
660 luau_stack_precondition!(self.check_stack(1));
661
662 unsafe {
663 let ptr: *mut u8 = lua_newbuffer(self.state, size) as _;
664
665 std::slice::from_raw_parts_mut(ptr, size)
666 }
667 }
668
669 pub fn push_buffer_from_slice(&mut self, slice: impl AsRef<[u8]>) -> &mut [u8] {
671 let slice = slice.as_ref();
674
675 let buffer = self.push_buffer(slice.len());
676 buffer.copy_from_slice(slice);
677
678 buffer
679 }
680
681 pub fn to_buffer(&mut self, idx: c_int) -> Option<&mut [u8]> {
683 luau_stack_precondition!(self.check_index(idx));
684
685 let mut len = 0;
686 let data: *mut u8 = unsafe { lua_tobuffer(self.state, idx, &mut len) as _ };
688
689 if !data.is_null() {
691 unsafe { Some(slice::from_raw_parts_mut(data, len)) }
693 } else {
694 None
695 }
696 }
697
698 pub fn to_buffer_ptr(&self, idx: c_int, len: &mut usize) -> *mut c_void {
700 luau_stack_precondition!(self.check_index(idx));
701
702 unsafe { lua_tobuffer(self.state, idx, len) }
704 }
705
706 pub fn create_table(&self) {
708 unsafe {
709 lua_createtable(self.state, 0, 0);
710 }
711 }
712
713 pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) {
715 unsafe {
716 lua_createtable(self.state, narr, nrec);
717 }
718 }
719
720 pub fn shift(&self, to: c_int) {
721 luau_stack_precondition!(self.check_index(to));
722
723 unsafe {
724 lua_insert(self.state, to);
725 }
726 }
727
728 pub fn reference(&self, idx: c_int) -> RefIndex {
730 luau_stack_precondition!(self.check_index(idx));
731
732 unsafe { lua_ref(self.state, idx) }
734 }
735
736 pub fn get_reference(&self, ref_index: RefIndex) -> LuauType {
738 luau_stack_precondition!(self.check_stack(1));
739
740 unsafe { lua_getref(self.state, ref_index) }
742 }
743
744 pub fn unreference(&self, ref_index: RefIndex) {
746 unsafe {
747 lua_unref(self.state, ref_index);
748 }
749 }
750
751 pub fn is_table(&self, idx: c_int) -> bool {
753 self.type_of(idx) == LuauType::LUA_TTABLE
754 }
755
756 pub fn set_field(&self, idx: c_int, field: impl AsRef<[u8]>) {
760 luau_stack_precondition!(self.check_stack(1));
761
762 let idx = if idx < 0 && !lua_ispseudo(idx) && !(idx == -1 && self.top() == 1) {
764 idx - 1
765 } else {
766 idx
767 };
768
769 self.push_string(field);
770 self.shift(-2);
771
772 self.set_table(idx);
773 }
774
775 pub fn raw_set_field(&self, idx: c_int, field: &str) {
779 luau_stack_precondition!(self.check_stack(1));
780
781 let idx = if idx < 0 && !lua_ispseudo(idx) && !(idx == -1 && self.top() == 1) {
782 idx - 1
783 } else {
784 idx
785 };
786
787 self.push_string(field);
788 self.shift(-2);
789
790 self.raw_set_table(idx);
791 }
792
793 pub fn set_table(&self, idx: c_int) {
797 assert!(
798 self.top() >= 2,
799 "There must be a key and value on the stack to set table"
800 );
801 luau_stack_precondition!(self.check_index(idx));
802
803 unsafe {
805 lua_settable(self.state, idx);
806 }
807 }
808
809 pub fn raw_set_table(&self, idx: c_int) {
813 assert!(
814 self.top() >= 2,
815 "There must be a key and value on the stack to set table"
816 );
817 luau_stack_precondition!(self.check_index(idx));
818
819 unsafe {
821 lua_rawset(self.state, idx);
822 }
823 }
824
825 pub fn get_field(&self, idx: c_int, field: impl AsRef<[u8]>) {
829 luau_stack_precondition!(self.check_index(idx));
830 luau_stack_precondition!(self.check_stack(1));
831
832 let idx = if idx < 0 && !lua_ispseudo(idx) {
833 idx - 1
834 } else {
835 idx
836 };
837
838 self.push_string(field);
839 self.get_table(idx);
840 }
841
842 pub fn raw_get_field(&self, idx: c_int, field: impl AsRef<[u8]>) {
846 luau_stack_precondition!(self.check_index(idx));
847 luau_stack_precondition!(self.check_stack(1));
848
849 let idx = if idx < 0 && !lua_ispseudo(idx) {
850 idx - 1
851 } else {
852 idx
853 };
854
855 self.push_string(field);
856 self.raw_get_table(idx);
857 }
858
859 pub fn get_table(&self, idx: c_int) {
863 assert!(
864 self.top() >= 1,
865 "There must be a key on the stack to index the table"
866 );
867 luau_stack_precondition!(self.check_index(idx));
868
869 unsafe {
871 lua_gettable(self.state, idx);
872 }
873 }
874
875 pub fn raw_get_table(&self, idx: c_int) {
879 assert!(
880 self.top() >= 1,
881 "There must be a key on the stack to index the table"
882 );
883 luau_stack_precondition!(self.check_index(idx));
884
885 unsafe {
887 lua_rawget(self.state, idx);
888 }
889 }
890
891 pub fn set_readonly(&self, idx: c_int, enabled: bool) {
893 assert!(self.is_table(idx));
894
895 unsafe {
897 lua_setreadonly(self.state, idx, enabled as c_int);
898 }
899 }
900
901 pub fn set_metatable(&self, idx: c_int) {
905 luau_stack_precondition!(self.check_index(idx));
906
907 unsafe {
908 lua_setmetatable(self.state, idx);
909 }
910 }
911
912 pub fn is_vector(&self, idx: c_int) -> bool {
914 self.type_of(idx) == LuauType::LUA_TVECTOR
915 }
916
917 pub fn push_vector(&self, x: f32, y: f32, z: f32, #[cfg(feature = "luau_vector4")] w: f32) {
919 luau_stack_precondition!(self.check_stack(1));
920
921 unsafe {
923 #[cfg(not(feature = "luau_vector4"))]
924 lua_pushvector(self.state, x, y, z);
925 #[cfg(feature = "luau_vector4")]
926 lua_pushvector(self.state, x, y, z, w);
927 }
928 }
929
930 #[cfg(not(feature = "luau_vector4"))]
931 pub fn to_vector(&self, idx: c_int) -> Option<(f32, f32, f32)> {
933 luau_stack_precondition!(self.check_index(idx));
934 unsafe {
935 Option::from(lua_tovector(self.state, idx)).map(|ptr| (*ptr, *ptr.add(1), *ptr.add(2)))
936 }
937 }
938
939 #[cfg(feature = "luau_vector4")]
940 pub fn to_vector(&self, idx: c_int) -> Option<(f32, f32, f32, f32)> {
942 luau_stack_precondition!(self.check_index(idx));
943 unsafe {
944 Option::from(lua_tovector(self.state, idx))
945 .map(|ptr| (*ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3)))
946 }
947 }
948
949 pub fn is_thread(&self, idx: c_int) -> bool {
951 self.type_of(idx) == LuauType::LUA_TTHREAD
952 }
953
954 pub fn new_thread(&self) -> LuauThread {
955 unsafe {
956 let thread_ptr = lua_newthread(self.state);
957 LuauThread::from_ptr(thread_ptr, self.get_associated().main_thread_rc.clone())
958 }
959 }
960
961 pub fn get_thread(&self, idx: c_int) -> Option<LuauThread> {
962 let ptr = unsafe { lua_tothread(self.state, idx) };
963
964 if !ptr.is_null() {
965 unsafe {
966 Some(LuauThread::from_ptr(
967 ptr,
968 self.get_associated().main_thread_rc.clone(),
969 ))
970 }
971 } else {
972 None
973 }
974 }
975
976 pub fn get_thread_data<T: Any>(&self) -> Option<&T> {
978 let boxed = unsafe { (lua_getthreaddata(self.state) as *const Box<dyn Any>).as_ref()? };
979
980 boxed.downcast_ref()
981 }
982
983 pub fn set_thread_data<T: Any>(&self, userdata: T) {
985 let b: Box<dyn Any> = Box::new(userdata);
986
987 unsafe {
988 lua_setthreaddata(self.state, Box::into_raw(b) as _);
989 }
990 }
991
992 pub fn resume(&self, luau_thread: &LuauThread, nargs: c_int) -> LuauStatus {
996 unsafe { lua_resume(luau_thread.get_state().state, self.state, nargs) }
997 }
998
999 pub fn is_function(&self, idx: c_int) -> bool {
1001 self.type_of(idx) == LuauType::LUA_TFUNCTION
1002 }
1003
1004 pub unsafe fn push_raw_function(
1011 &self,
1012 func: CFunction,
1013 debug_name: Option<&CStr>,
1014 num_upvals: c_int,
1015 continuation: Option<LuaContinuation>,
1016 ) {
1017 luau_stack_precondition!(self.check_stack(1));
1018
1019 assert!(
1020 self.top() >= num_upvals,
1021 "The number of upvalues for a raw function must not exceed the stack length"
1022 );
1023
1024 unsafe {
1026 lua_pushcclosurek(
1027 self.state,
1028 func,
1029 if let Some(name) = debug_name {
1030 name.as_ptr()
1031 } else {
1032 null()
1033 },
1034 num_upvals,
1035 continuation,
1036 );
1037 }
1038 }
1039
1040 pub fn push_function_continuation<
1044 F: FnMut(&Luau) -> c_int,
1045 Cont: FnMut(&Luau, LuauStatus) -> c_int,
1046 >(
1047 &self,
1048 func: F,
1049 debug_name: Option<&CStr>,
1050 num_upvals: c_int,
1051 cont: Cont,
1052 ) {
1053 assert!(
1054 self.top() >= num_upvals,
1055 "The number of upvalues for a raw function must not exceed the stack length"
1056 );
1057
1058 luau_stack_precondition!(self.check_stack(2));
1059
1060 struct CallState<F, Cont> {
1061 func: F,
1062 cont: Cont,
1063 }
1064
1065 let call_state = Box::new(CallState { func, cont });
1066
1067 unsafe extern "C-unwind" fn invoke_fn<
1068 F: FnMut(&Luau) -> c_int,
1069 Cont: FnMut(&Luau, LuauStatus) -> c_int,
1070 >(
1071 state: *mut _LuaState,
1072 ) -> c_int {
1073 let call_state =
1074 lua_tolightuserdata(state, lua_upvalueindex(1)).cast::<CallState<F, Cont>>();
1075
1076 let luau = Luau::from_ptr(state);
1077
1078 ((*call_state).func)(&luau)
1079 }
1080
1081 unsafe extern "C-unwind" fn invoke_continuation<
1082 F: FnMut(&Luau) -> c_int,
1083 Cont: FnMut(&Luau, LuauStatus) -> c_int,
1084 >(
1085 state: *mut _LuaState,
1086 status: c_int,
1087 ) -> c_int {
1088 let call_state =
1089 lua_tolightuserdata(state, lua_upvalueindex(1)).cast::<CallState<F, Cont>>();
1090
1091 let luau = Luau::from_ptr(state);
1092 ((*call_state).cont)(&luau, std::mem::transmute::<c_int, LuauStatus>(status))
1093 }
1094
1095 unsafe {
1096 lua_pushlightuserdata(self.state, Box::into_raw(call_state) as _);
1097
1098 self.push_raw_function(
1099 invoke_fn::<F, Cont>,
1100 debug_name,
1101 1 + num_upvals,
1102 Some(invoke_continuation::<F, Cont>),
1103 );
1104 }
1105 }
1106
1107 pub fn push_function<F: FnMut(&Luau) -> i32>(
1111 &self,
1112 func: F,
1113 debug_name: Option<&CStr>,
1114 num_upvals: c_int,
1115 ) {
1116 assert!(
1117 self.top() >= num_upvals,
1118 "The number of upvalues for a raw function must not exceed the stack length"
1119 );
1120
1121 luau_stack_precondition!(self.check_stack(2));
1122
1123 let func_box = Box::new(func);
1124
1125 unsafe extern "C-unwind" fn invoke_fn<T: FnMut(&Luau) -> i32>(
1126 state: *mut _LuaState,
1127 ) -> c_int {
1128 let func = lua_tolightuserdata(state, lua_upvalueindex(1)).cast::<T>();
1129
1130 let state = Luau::from_ptr(state);
1131 (*func)(&state)
1132 }
1133
1134 unsafe {
1135 lua_pushlightuserdata(self.state, Box::into_raw(func_box) as _);
1136
1137 self.push_raw_function(invoke_fn::<F>, debug_name, 1 + num_upvals, None);
1138 }
1139 }
1140
1141 pub fn call(&self, nargs: c_int, nresults: c_int) -> LuauStatus {
1143 assert!(
1144 self.is_function(-1),
1145 "The value at top of stack must be a function"
1146 );
1147
1148 assert!(
1149 self.top() >= nargs,
1150 "Argument count may not exceed the total stack size"
1151 );
1152
1153 luau_stack_precondition!(self.check_stack(nresults));
1154
1155 unsafe { lua_pcall(self.state, nargs, nresults, 0) }
1156 }
1157
1158 pub fn load(&self, chunk_name: Option<&CStr>, bytecode: &[u8], env: c_int) -> Result<(), &str> {
1160 luau_stack_precondition!(env == 0 || self.check_index(env));
1162 luau_stack_precondition!(self.check_stack(2));
1163
1164 let success = unsafe {
1165 luau_load(
1166 self.state,
1167 chunk_name.unwrap_or(c"").as_ptr(),
1168 bytecode.as_ptr() as _,
1169 bytecode.len(),
1170 env,
1171 )
1172 };
1173
1174 if success == 0 {
1175 Ok(())
1176 } else {
1177 dbg!(self.top());
1178 Err(self.to_str(-1).unwrap().unwrap())
1180 }
1181 }
1182
1183 #[cfg(feature = "codegen")]
1184 pub fn codegen(&self, idx: c_int) {
1188 luau_stack_precondition!(self.check_index(idx));
1189 assert!(
1190 self.is_function(idx),
1191 "The value at idx must be a function to be compiled with codegen"
1192 );
1193
1194 unsafe {
1195 luau_codegen_compile(self.state, idx);
1196 }
1197 }
1198}
1199
1200unsafe extern "C-unwind" fn fatal_runtime_error_handler(state: *mut _LuaState) -> c_int {
1202 let luau = unsafe { Luau::from_ptr(state) };
1203
1204 panic!(
1205 "Uncaught runtime error - \"{}\"",
1206 String::from_utf8_lossy(luau.convert_to_str_slice(-1))
1207 );
1208}
1209
1210unsafe extern "C-unwind" fn fatal_error_handler(state: *mut _LuaState, status: LuauStatus) {
1212 match status {
1213 LuauStatus::LUA_ERRRUN => fatal_runtime_error_handler(state),
1215 LuauStatus::LUA_ERRMEM => std::process::abort(),
1217 LuauStatus::LUA_ERRERR => panic!("Error originating from error handling mechanism"),
1219 _ => unreachable!(),
1221 };
1222
1223 panic!("Fatal error in Luau execution");
1224}
1225
1226impl Default for Luau {
1227 fn default() -> Self {
1228 Self::new(DefaultLuauAllocator {})
1229 }
1230}
1231
1232impl Drop for Luau {
1233 fn drop(&mut self) {
1234 if !self.owned {
1235 return;
1236 }
1237
1238 unsafe {
1239 let mut associated: *mut AssociatedData = null_mut();
1240 lua_getallocf(self.state, &raw mut associated as _);
1241
1242 let associated_owned = Box::from_raw(associated);
1243
1244 associated_owned.main_thread_rc.set(false);
1246
1247 lua_close(self.state);
1248
1249 _ = associated_owned
1250 }
1251 }
1252}
1253
1254#[macro_export]
1255macro_rules! try_luau {
1256 ($state:ident, $block:block) => {{
1257 $state.push_function(|$state| $block, Some(c"_try_lua"), 0);
1258 $state.call(0, 0)
1259 }};
1260}
1261
1262#[cfg(test)]
1263#[allow(non_snake_case)]
1264mod tests {
1265 use std::{
1266 ffi::{c_int, c_void},
1267 hint::black_box,
1268 rc::Rc,
1269 };
1270
1271 use crate::{
1272 Luau, LuauAllocator, _LuaState,
1273 compile::Compiler,
1274 lua_error, lua_tonumber, lua_upvalueindex,
1275 userdata::{UserdataBorrowError, UserdataRef},
1276 LuauLibs, LuauStatus, LuauType,
1277 };
1278
1279 #[test]
1280 fn try_test() {
1281 let luau = Luau::default();
1282
1283 let status = try_luau!(luau, {
1284 luau.push_boolean(true);
1285 luau.error()
1286 });
1287
1288 assert!(
1289 matches!(status, LuauStatus::LUA_ERRRUN),
1290 "Expected a runtime error"
1291 );
1292 assert!(luau.to_boolean(-1), "Expected the boolean to be true");
1293 }
1294
1295 #[test]
1296 #[should_panic]
1297 fn stack_checking_no_value() {
1298 let luau = Luau::default();
1299
1300 luau.is_number(1);
1301 }
1302
1303 #[test]
1304 #[should_panic]
1305 fn stack_checking_neg_no_value() {
1306 let luau = Luau::default();
1307
1308 luau.is_number(-1);
1309 }
1310
1311 #[test]
1312 fn stack_checking_has_value() {
1313 let luau = Luau::default();
1314
1315 luau.push_number(0.0);
1316
1317 luau.is_number(-1);
1318 luau.is_number(1);
1319 }
1320
1321 #[cfg(all(feature = "codegen", feature = "compiler"))]
1322 #[test]
1323 fn codegen() {
1324 use crate::compile::Compiler;
1325
1326 let compiler = Compiler::new();
1327 let luau = Luau::default();
1328
1329 let result = compiler.compile("(function() return 123 end)()");
1330
1331 assert!(result.is_ok(), "Compiler result is expected to be OK");
1332
1333 let load_result = luau.load(None, result.bytecode().unwrap(), 0);
1334
1335 assert!(load_result.is_ok(), "Load result should be Ok");
1336
1337 let load_result = luau.load(Some(c"test"), result.bytecode().unwrap(), 0);
1338
1339 assert!(load_result.is_ok(), "Load result should be Ok");
1340
1341 luau.codegen(-1);
1342
1343 luau.call(0, 0);
1344 }
1345
1346 #[test]
1347 fn load_error() {
1348 let luau = Luau::default();
1349
1350 let load_result = luau.load(None, b"\0Error!", 0);
1351
1352 assert!(
1354 load_result.is_err_and(|v| v == r#"[string ""]Error!"#),
1355 "Expected load result to be an error and be the correct error message."
1356 );
1357 }
1358
1359 #[test]
1360 fn load_libs() {
1361 let luau = Luau::default();
1362
1363 luau.load_libs(LuauLibs::ALL_LIBS);
1364
1365 luau.get_field(luau.globals(), "table");
1366
1367 assert_eq!(luau.type_of(-1), LuauType::LUA_TTABLE);
1368
1369 luau.get_field(luau.globals(), "print");
1370
1371 assert_eq!(luau.type_of(-1), LuauType::LUA_TFUNCTION);
1372 }
1373
1374 #[test]
1375 fn tables() {
1376 let luau = Luau::default();
1377
1378 luau.create_table();
1379
1380 luau.push_number(123.0);
1381
1382 luau.set_field(-2, "abc");
1383
1384 luau.get_field(-1, "abc");
1385
1386 assert_eq!(luau.to_number(-1), Some(123.0));
1387
1388 luau.pop(1);
1389
1390 luau.set_field(-1, "a");
1392 }
1393
1394 #[test]
1395 fn metatables() {
1396 let luau = Luau::default();
1397
1398 luau.create_table();
1399 luau.create_table();
1400
1401 let mut called: Option<String> = None;
1402 luau.push_function(
1403 |luau| {
1404 called = luau.to_str(-1).map(Result::unwrap).map(str::to_string);
1405 0
1406 },
1407 None,
1408 0,
1409 );
1410 luau.set_field(-2, "__index");
1411 luau.set_metatable(-2);
1412
1413 let index = "Hello!".to_string();
1414 luau.get_field(-1, &index);
1415
1416 assert_eq!(called, Some(index));
1417 }
1418
1419 #[test]
1420 #[should_panic]
1421 fn unhandled_error() {
1422 let luau = Luau::default();
1423
1424 luau.push_string("hello error!");
1425
1426 unsafe {
1427 lua_error(luau.to_ptr());
1428 }
1429 }
1430
1431 #[test]
1432 fn pop() {
1433 let luau = Luau::default();
1434
1435 luau.push_number(0.0);
1436
1437 assert_eq!(luau.top(), 1);
1438
1439 luau.pop(1);
1440
1441 assert_eq!(luau.top(), 0);
1442
1443 luau.push_number(0.0);
1444 luau.push_number(0.0);
1445
1446 assert_eq!(luau.top(), 2);
1447
1448 luau.pop(2);
1449
1450 assert_eq!(luau.top(), 0);
1451 }
1452
1453 #[test]
1454 fn threads() {
1455 let luau = Luau::default();
1456
1457 let thread = luau.new_thread();
1458 let thread_state = thread.get_state();
1459
1460 let mut was_called = false;
1461
1462 thread_state.push_function(
1463 |_| {
1464 was_called = true;
1465 0
1466 },
1467 None,
1468 0,
1469 );
1470
1471 luau.resume(&thread, 0);
1472
1473 assert!(was_called, "Expected thread function to be called");
1474 }
1475
1476 #[test]
1477 fn app_data() {
1478 let luau = Luau::default();
1479
1480 luau.set_app_data(Some(true));
1481
1482 assert_eq!(luau.get_app_data::<bool>().copied(), Some(true))
1483 }
1484
1485 #[test]
1486 fn function_upvalue_test() {
1487 let luau = Luau::default();
1488
1489 luau.push_number(1.0);
1490 luau.push_number(2.0);
1491 luau.push_number(3.0);
1492
1493 luau.push_function(
1494 |luau| {
1495 assert_eq!(luau.to_number(luau.upvalue(1)), Some(1.0));
1496 assert_eq!(luau.to_number(luau.upvalue(2)), Some(2.0));
1497 assert_eq!(luau.to_number(luau.upvalue(3)), Some(3.0));
1498
1499 0
1500 },
1501 Some(c"test"),
1502 3,
1503 );
1504
1505 luau.call(0, 0);
1506 }
1507
1508 #[test]
1509 fn raw_function_upvalue_test() {
1510 let luau = Luau::default();
1511
1512 luau.push_number(1.0);
1513 luau.push_number(2.0);
1514 luau.push_number(3.0);
1515
1516 unsafe extern "C-unwind" fn test_extern_fn(state: *mut _LuaState) -> c_int {
1517 assert_eq!(lua_tonumber(state, lua_upvalueindex(1)), 1.0);
1518 assert_eq!(lua_tonumber(state, lua_upvalueindex(2)), 2.0);
1519 assert_eq!(lua_tonumber(state, lua_upvalueindex(3)), 3.0);
1520
1521 0
1522 }
1523
1524 unsafe {
1525 luau.push_raw_function(test_extern_fn, Some(c"test"), 3, None);
1526 }
1527
1528 luau.call(0, 0);
1529 }
1530
1531 #[test]
1532 fn continuations() {
1533 let luau = Luau::default();
1534 let compiler = Compiler::new();
1535
1536 let bc = compiler.compile("(...)()");
1537
1538 let thread = luau.new_thread();
1539 let thread_state = thread.get_state();
1540
1541 let mut cont = false;
1542
1543 thread_state.push_function_continuation(
1544 |l| l.yield_luau(0),
1545 None,
1546 0,
1547 |_, _| {
1548 cont = true;
1549 0
1550 },
1551 );
1552 thread_state.load(None, bc.bytecode().unwrap(), 0).unwrap();
1553
1554 luau.resume(&thread, 1);
1555 luau.resume(&thread, 0);
1556
1557 assert!(cont, "Expected that the continuation would be called.")
1558 }
1559
1560 #[test]
1561 #[should_panic]
1562 fn luau_panic_unwind() {
1563 struct PanicAllocator;
1564
1565 impl LuauAllocator for PanicAllocator {
1566 fn allocate(&self, _: usize) -> *mut std::ffi::c_void {
1567 panic!()
1568 }
1569
1570 fn reallocate(&self, _: *mut c_void, _: usize, _: usize) -> *mut std::ffi::c_void {
1571 panic!()
1572 }
1573
1574 fn deallocate(&self, _: *mut c_void, _: usize) {
1575 panic!()
1576 }
1577 }
1578
1579 {
1580 black_box(Luau::new(PanicAllocator {}));
1581 };
1582 }
1583
1584 #[test]
1585 fn function_check() {
1586 let luau = Luau::default();
1587
1588 luau.push_function(
1589 |l| {
1590 l.check_args(1, None);
1591
1592 0
1593 },
1594 None,
1595 0,
1596 );
1597
1598 let status = luau.call(0, 0);
1599
1600 assert!(
1601 matches!(status, LuauStatus::LUA_ERRRUN),
1602 "Expected there to be a runtime error."
1603 );
1604
1605 luau.push_function(
1606 |l| {
1607 l.check_args(1, None);
1608
1609 0
1610 },
1611 Some(c"test"),
1612 0,
1613 );
1614
1615 let status = luau.call(0, 0);
1616
1617 assert!(
1618 matches!(status, LuauStatus::LUA_ERRRUN),
1619 "Expected there to be a runtime error."
1620 );
1621
1622 }
1623
1624 #[test]
1625 fn userdata_borrow() {
1626 let luau = Luau::default();
1627
1628 luau.push_userdata(());
1629
1630 {
1631 let borrow = luau.try_borrow_userdata_mut::<()>(-1);
1632
1633 assert!(
1634 borrow.as_ref().is_some_and(Result::is_ok),
1635 "Expected mutable borrow for userdata to be valid"
1636 );
1637
1638 assert!(
1639 matches!(
1640 luau.try_borrow_userdata::<()>(-1),
1641 Some(Err(UserdataBorrowError::AlreadyMutable))
1642 ),
1643 "Expected immutable borrow for userdata to be invalid"
1644 );
1645
1646 assert!(
1647 matches!(
1648 luau.try_borrow_userdata_mut::<()>(-1),
1649 Some(Err(UserdataBorrowError::AlreadyMutable))
1650 ),
1651 "Expected mutable borrow for userdata to be invalid"
1652 );
1653
1654 drop(borrow);
1655
1656 assert!(
1657 matches!(luau.try_borrow_userdata_mut::<()>(-1), Some(Ok(_))),
1658 "Expected mutable borrow for userdata to be valid"
1659 );
1660 }
1661
1662 {
1663 let borrow = luau.try_borrow_userdata::<()>(-1);
1664
1665 assert!(
1666 matches!(borrow, Some(Ok(_))),
1667 "Expected to be a valid borrow"
1668 );
1669
1670 assert!(
1671 matches!(
1672 luau.try_borrow_userdata_mut::<()>(-1),
1673 Some(Err(UserdataBorrowError::AlreadyImmutable))
1674 ),
1675 "Expected borrow to be an AlreadyImmutable error"
1676 );
1677 }
1678 }
1679
1680 #[test]
1681 fn userdata_values() {
1682 let luau = Luau::default();
1683
1684 luau.push_userdata(());
1685
1686 let mut vec = Vec::with_capacity(128);
1687 for i in 0..128 {
1688 vec.push(i);
1689 }
1690
1691 luau.push_userdata(vec);
1692
1693 #[repr(transparent)]
1694 struct DropCheck(Rc<bool>);
1695
1696 let drop_rc = Rc::new(true);
1697 let yes_drop = DropCheck(drop_rc.clone());
1698
1699 luau.push_userdata(yes_drop);
1700
1701 assert!(luau.borrow_userdata(-3).is_some_and(
1702 #[allow(clippy::unit_cmp)]
1703 |v: UserdataRef<()>| *v == ()
1704 ));
1705
1706 assert!(luau
1707 .borrow_userdata::<Vec<i32>>(-2)
1708 .is_some_and(|v| v.is_sorted())); assert!(luau.borrow_userdata::<DropCheck>(-1).is_some());
1711
1712 drop(luau);
1713
1714 }
1716
1717 #[test]
1718 fn string_values() {
1719 let luau = Luau::default();
1720
1721 const TEST_CONST: &[u8] = &[0xCA, 0xFE, 0xBA, 0xBE];
1722 const INVALID_SEQUENCE: &[u8] = &[0xC3, 0x28];
1723
1724 luau.push_string("Hello, world!");
1725 luau.push_string(TEST_CONST);
1726 luau.push_number(12345.0f64);
1727 luau.push_string(INVALID_SEQUENCE);
1728
1729 assert_eq!(luau.to_str_slice(-4), Some(b"Hello, world!" as _));
1730 assert_eq!(luau.to_str(-4), Some(Ok("Hello, world!")));
1731 assert_eq!(luau.to_str_slice(1), Some(b"Hello, world!" as _));
1732 assert_eq!(luau.to_str(1), Some(Ok("Hello, world!")));
1733
1734 assert_eq!(luau.to_str_slice(-3), Some(TEST_CONST));
1735 assert_eq!(luau.to_str_slice(2), Some(TEST_CONST));
1736
1737 assert_eq!(luau.to_str_slice(-2), Some(b"12345" as _));
1738 assert_eq!(luau.to_str(-2), Some(Ok("12345")));
1739 assert_eq!(luau.to_str_slice(3), Some(b"12345" as _));
1740 assert_eq!(luau.to_str(3), Some(Ok("12345")));
1741
1742 assert_eq!(luau.to_str_slice(-1), Some(INVALID_SEQUENCE));
1743 assert!(luau.to_str(-1).is_some_and(|r| r.is_err()));
1744 assert_eq!(luau.to_str_slice(4), Some(INVALID_SEQUENCE));
1745 assert!(luau.to_str(4).is_some_and(|r| r.is_err()));
1746 }
1747
1748 #[test]
1749 fn numeric_values() {
1750 let luau = Luau::default();
1751
1752 luau.push_number(f64::NAN);
1753 luau.push_number(f64::INFINITY);
1754 luau.push_number(f64::EPSILON);
1755 luau.push_string("12345");
1756
1757 assert_eq!(
1759 luau.to_number(-4).map(f64::to_bits),
1760 Some(f64::NAN.to_bits())
1761 );
1762 assert_eq!(
1763 luau.to_number(1).map(f64::to_bits),
1764 Some(f64::NAN.to_bits())
1765 );
1766
1767 assert_eq!(luau.to_number(-3), Some(f64::INFINITY));
1768 assert_eq!(luau.to_number(2), Some(f64::INFINITY));
1769
1770 assert_eq!(luau.to_number(-2), Some(f64::EPSILON));
1771 assert_eq!(luau.to_number(3), Some(f64::EPSILON));
1772
1773 assert_eq!(luau.to_number(-1), Some(12345.0f64));
1774 assert_eq!(luau.to_number(4), Some(12345.0f64));
1775 }
1776}