1use std::cell::RefCell;
22use std::collections::hash_map::Entry;
23use std::collections::HashMap;
24use std::hash::{BuildHasherDefault, Hasher};
25use std::rc::Rc;
26
27use crate::string::StringPool;
28pub use lua_types::error::LuaError;
29pub use lua_types::{CallInfoIdx, StackIdx};
30
31pub struct StackIdxConv(pub StackIdx);
34
35#[inline(always)]
40pub fn stack_idx_to_i32(i: StackIdx) -> i32 {
41 i.0 as i32
42}
43
44impl From<u32> for StackIdxConv {
45 #[inline(always)]
46 fn from(v: u32) -> Self {
47 StackIdxConv(StackIdx(v))
48 }
49}
50impl From<i32> for StackIdxConv {
51 #[inline(always)]
52 fn from(v: i32) -> Self {
53 StackIdxConv(StackIdx(v.max(0) as u32))
54 }
55}
56impl From<usize> for StackIdxConv {
57 #[inline(always)]
58 fn from(v: usize) -> Self {
59 StackIdxConv(StackIdx(v as u32))
60 }
61}
62impl From<StackIdx> for StackIdxConv {
63 #[inline(always)]
64 fn from(v: StackIdx) -> Self {
65 StackIdxConv(v)
66 }
67}
68pub use lua_types::closure::{
69 LuaCClosure as LuaClosureC, LuaCFnPtr, LuaClosure, LuaLClosure as LuaClosureLua,
70};
71pub use lua_types::gc::GcRef;
72pub use lua_types::proto::LuaProto;
73pub use lua_types::string::LuaString;
74pub use lua_types::upval::{UpVal, UpValState};
75pub use lua_types::userdata::LuaUserData;
76pub use lua_types::value::{F2Imod, LuaTable, LuaValue};
77
78pub struct LuaByteHasher {
79 hash: u64,
80}
81
82impl Default for LuaByteHasher {
83 fn default() -> Self {
84 Self {
85 hash: 0xcbf2_9ce4_8422_2325,
86 }
87 }
88}
89
90impl Hasher for LuaByteHasher {
91 #[inline]
92 fn write(&mut self, bytes: &[u8]) {
93 const PRIME: u64 = 0x0000_0100_0000_01b3;
94 for &byte in bytes {
95 self.hash ^= u64::from(byte);
96 self.hash = self.hash.wrapping_mul(PRIME);
97 }
98 }
99
100 #[inline]
101 fn write_u8(&mut self, i: u8) {
102 self.write(&[i]);
103 }
104
105 #[inline]
106 fn write_usize(&mut self, i: usize) {
107 self.write(&i.to_ne_bytes());
108 }
109
110 #[inline]
111 fn finish(&self) -> u64 {
112 self.hash
113 }
114}
115
116pub type LuaByteBuildHasher = BuildHasherDefault<LuaByteHasher>;
117pub type InternedStringMap = HashMap<Box<[u8]>, GcRef<LuaString>, LuaByteBuildHasher>;
118
119pub type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
126
127pub type LuaRustFunction = Rc<dyn Fn(&mut LuaState) -> Result<usize, LuaError>>;
128
129#[derive(Clone)]
130pub enum LuaCallable {
131 Bare(LuaCFunction),
132 Rust(LuaRustFunction),
133}
134
135impl std::fmt::Debug for LuaCallable {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 LuaCallable::Bare(_) => f.write_str("LuaCallable::Bare(..)"),
139 LuaCallable::Rust(_) => f.write_str("LuaCallable::Rust(..)"),
140 }
141 }
142}
143
144impl LuaCallable {
145 pub fn bare(f: LuaCFunction) -> Self {
146 LuaCallable::Bare(f)
147 }
148
149 pub fn rust(f: LuaRustFunction) -> Self {
150 LuaCallable::Rust(f)
151 }
152
153 pub fn as_bare(&self) -> Option<LuaCFunction> {
154 match self {
155 LuaCallable::Bare(f) => Some(*f),
156 LuaCallable::Rust(_) => None,
157 }
158 }
159
160 pub fn call(&self, state: &mut LuaState) -> Result<usize, LuaError> {
161 match self {
162 LuaCallable::Bare(f) => f(state),
163 LuaCallable::Rust(f) => f(state),
164 }
165 }
166}
167
168#[derive(Clone, Debug)]
169pub enum FinalizerObject {
170 Table(GcRef<LuaTable>),
171 UserData(GcRef<LuaUserData>),
172}
173
174impl FinalizerObject {
175 pub fn identity(&self) -> usize {
176 match self {
177 FinalizerObject::Table(t) => t.identity(),
178 FinalizerObject::UserData(u) => u.identity(),
179 }
180 }
181
182 pub fn metatable(&self) -> Option<GcRef<LuaTable>> {
183 match self {
184 FinalizerObject::Table(t) => t.metatable(),
185 FinalizerObject::UserData(u) => u.metatable(),
186 }
187 }
188
189 pub fn as_lua_value(&self) -> LuaValue {
190 match self {
191 FinalizerObject::Table(t) => LuaValue::Table(t.clone()),
192 FinalizerObject::UserData(u) => LuaValue::UserData(u.clone()),
193 }
194 }
195
196 pub fn mark(&self, marker: &mut lua_gc::Marker) {
197 match self {
198 FinalizerObject::Table(t) => marker.mark(t.0),
199 FinalizerObject::UserData(u) => marker.mark(u.0),
200 }
201 }
202
203 pub fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
204 Some(match self {
205 FinalizerObject::Table(t) => t.0.as_trace_ptr(),
206 FinalizerObject::UserData(u) => u.0.as_trace_ptr(),
207 })
208 }
209
210 pub fn age(&self) -> lua_gc::GcAge {
211 match self {
212 FinalizerObject::Table(t) => t.0.age(),
213 FinalizerObject::UserData(u) => u.0.age(),
214 }
215 }
216
217 pub fn is_finalized(&self) -> bool {
218 match self {
219 FinalizerObject::Table(t) => t.0.is_finalized(),
220 FinalizerObject::UserData(u) => u.0.is_finalized(),
221 }
222 }
223
224 pub fn set_finalized(&self, finalized: bool) {
225 match self {
226 FinalizerObject::Table(t) => t.0.set_finalized(finalized),
227 FinalizerObject::UserData(u) => u.0.set_finalized(finalized),
228 }
229 }
230}
231
232impl lua_gc::FinalizerEntry for FinalizerObject {
233 fn identity(&self) -> usize {
234 FinalizerObject::identity(self)
235 }
236
237 fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
238 FinalizerObject::heap_ptr(self)
239 }
240
241 fn age(&self) -> lua_gc::GcAge {
242 FinalizerObject::age(self)
243 }
244
245 fn is_finalized(&self) -> bool {
246 FinalizerObject::is_finalized(self)
247 }
248
249 fn set_finalized(&self, finalized: bool) {
250 FinalizerObject::set_finalized(self, finalized);
251 }
252}
253
254#[derive(Clone, Debug)]
255pub struct WeakTableEntry {
256 table: lua_types::gc::GcWeak<LuaTable>,
257 kind: lua_gc::WeakListKind,
258}
259
260impl WeakTableEntry {
261 pub fn new(table: &GcRef<LuaTable>) -> Self {
262 let mode = table.weak_mode();
263 let weak_keys = (mode & (1 << 0)) != 0;
264 let weak_values = (mode & (1 << 1)) != 0;
265 let kind = match (weak_keys, weak_values) {
266 (true, true) => lua_gc::WeakListKind::AllWeak,
267 (true, false) => lua_gc::WeakListKind::Ephemeron,
268 (false, true) => lua_gc::WeakListKind::WeakValues,
269 (false, false) => lua_gc::WeakListKind::WeakValues,
270 };
271 Self {
272 table: table.downgrade(),
273 kind,
274 }
275 }
276}
277
278impl lua_gc::WeakEntry for WeakTableEntry {
279 type Strong = GcRef<LuaTable>;
280
281 fn identity(&self) -> usize {
282 self.table.identity()
283 }
284
285 fn list_kind(&self) -> lua_gc::WeakListKind {
286 self.kind
287 }
288
289 fn upgrade(&self) -> Option<Self::Strong> {
290 self.table.upgrade()
291 }
292}
293
294pub(crate) const EXTRA_STACK: usize = 5;
298
299pub(crate) const LUA_MINSTACK: usize = 20;
301
302pub(crate) const BASIC_STACK_SIZE: usize = 2 * LUA_MINSTACK;
304
305pub(crate) const LUAI_MAXCCALLS: u32 = 200;
325
326pub(crate) const CIST_C: u16 = 1 << 1;
328
329pub(crate) const CIST_OAH: u16 = 1 << 0;
331pub(crate) const CIST_FRESH: u16 = 1 << 2;
332pub(crate) const CIST_HOOKED: u16 = 1 << 3;
333pub(crate) const CIST_YPCALL: u16 = 1 << 4;
334pub(crate) const CIST_TAIL: u16 = 1 << 5;
335pub(crate) const CIST_HOOKYIELD: u16 = 1 << 6;
336pub(crate) const CIST_FIN: u16 = 1 << 7;
337pub(crate) const CIST_TRAN: u16 = 1 << 8;
338pub(crate) const CIST_RECST: u32 = 10;
339pub(crate) const CIST_LEQ: u16 = 1 << 13;
346
347const LUA_NUMTYPES: usize = 9;
349
350const GCSTPUSR: u8 = 1;
352const GCSTPGC: u8 = 2;
353
354const GCS_PAUSE: u8 = 0;
356
357const LUAI_GCPAUSE: u32 = 200;
358const LUAI_GCMUL: u32 = 100;
359const LUAI_GCSTEPSIZE: u8 = 13;
360const LUAI_GENMAJORMUL: u32 = 100;
361const LUAI_GENMINORMUL: u8 = 20;
362
363const WHITE0BIT: u8 = 0;
364
365const STRCACHE_N: usize = 53;
366const STRCACHE_M: usize = 2;
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
374pub enum GcKind {
375 Incremental = 0,
376 Generational = 1,
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387pub enum WarnMode {
388 Off,
389 On,
390 Cont,
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395pub enum TestWarnMode {
396 Normal,
397 Allow,
398 Store,
399}
400
401pub use lua_types::status::LuaStatus;
406
407#[derive(Clone)]
414pub struct StackValue {
415 pub val: LuaValue,
416 pub tbc_delta: u16,
417}
418
419impl Default for StackValue {
420 fn default() -> Self {
421 StackValue {
422 val: LuaValue::Nil,
423 tbc_delta: 0,
424 }
425 }
426}
427
428#[derive(Clone)]
437pub struct CallInfo {
438 pub func: StackIdx,
440
441 pub top: StackIdx,
443
444 pub previous: Option<CallInfoIdx>,
446
447 pub next: Option<CallInfoIdx>,
449
450 pub u: CallInfoFrame,
451
452 pub u2: CallInfoExtra,
453
454 pub nresults: i16,
456
457 pub callstatus: u16,
459
460 pub call_metamethods: u8,
464}
465
466#[derive(Clone, Copy)]
469pub enum CallInfoFrame {
470 Lua {
471 savedpc: u32,
473 trap: bool,
475 nextraargs: i32,
477 },
478 C {
479 k: Option<LuaKFunction>,
481 old_errfunc: isize,
483 ctx: isize,
485 },
486}
487
488pub type LuaKFunction = fn(&mut LuaState, status: i32, ctx: isize) -> Result<usize, LuaError>;
490
491#[derive(Default, Clone, Copy)]
495pub struct CallInfoExtra {
496 pub value: i32,
497 pub ftransfer: u16,
498 pub ntransfer: u16,
499}
500
501impl CallInfoFrame {
502 pub fn c_default() -> Self {
504 CallInfoFrame::C {
505 k: None,
506 old_errfunc: 0,
507 ctx: 0,
508 }
509 }
510
511 pub fn lua_default() -> Self {
513 CallInfoFrame::Lua {
514 savedpc: 0,
515 trap: false,
516 nextraargs: 0,
517 }
518 }
519}
520
521impl Default for CallInfo {
522 fn default() -> Self {
523 CallInfo {
524 func: StackIdx(0),
525 top: StackIdx(0),
526 previous: None,
527 next: None,
528 u: CallInfoFrame::c_default(),
529 u2: CallInfoExtra::default(),
530 nresults: 0,
531 callstatus: 0,
532 call_metamethods: 0,
533 }
534 }
535}
536
537impl CallInfo {
538 pub fn is_lua(&self) -> bool {
539 (self.callstatus & CIST_C) == 0
540 }
541 pub fn is_lua_code(&self) -> bool {
542 self.is_lua()
543 }
544 pub fn is_vararg_func(&self) -> bool {
551 false
552 }
553 pub fn saved_pc(&self) -> u32 {
554 if let CallInfoFrame::Lua { savedpc, .. } = self.u {
555 savedpc
556 } else {
557 0
558 }
559 }
560 pub fn set_saved_pc(&mut self, pc: u32) {
561 if let CallInfoFrame::Lua {
562 ref mut savedpc, ..
563 } = self.u
564 {
565 *savedpc = pc;
566 }
567 }
568 pub fn nextra_args(&self) -> i32 {
569 if let CallInfoFrame::Lua { nextraargs, .. } = self.u {
570 nextraargs
571 } else {
572 0
573 }
574 }
575 pub fn transfer_ftransfer(&self) -> u16 {
576 self.u2.ftransfer
577 }
578 pub fn transfer_ntransfer(&self) -> u16 {
579 self.u2.ntransfer
580 }
581 pub fn set_trap(&mut self, t: bool) {
582 if let CallInfoFrame::Lua { ref mut trap, .. } = self.u {
583 *trap = t;
584 }
585 }
586 pub fn recover_status(&self) -> i32 {
589 ((self.callstatus >> CIST_RECST) & 7) as i32
590 }
591 pub fn set_recover_status<T: Into<i32>>(&mut self, status: T) {
594 let st = (status.into() & 7) as u16;
595 self.callstatus = (self.callstatus & !(7u16 << CIST_RECST)) | (st << CIST_RECST);
596 }
597 pub fn get_oah(&self) -> bool {
598 (self.callstatus & CIST_OAH) != 0
599 }
600 pub fn set_oah(&mut self, allow: bool) {
603 self.callstatus = (self.callstatus & !CIST_OAH) | (if allow { CIST_OAH } else { 0 });
604 }
605 pub fn u_c_old_errfunc(&self) -> isize {
606 if let CallInfoFrame::C { old_errfunc, .. } = self.u {
607 old_errfunc
608 } else {
609 0
610 }
611 }
612 pub fn u_c_ctx(&self) -> isize {
613 if let CallInfoFrame::C { ctx, .. } = self.u {
614 ctx
615 } else {
616 0
617 }
618 }
619 pub fn u_c_k(&self) -> Option<LuaKFunction> {
620 if let CallInfoFrame::C { k, .. } = self.u {
621 k
622 } else {
623 None
624 }
625 }
626 pub fn set_u_c_k(&mut self, k: Option<LuaKFunction>) {
630 if let CallInfoFrame::C {
631 k: ref mut slot, ..
632 } = self.u
633 {
634 *slot = k;
635 }
636 }
637 pub fn set_u_c_ctx(&mut self, ctx: isize) {
639 if let CallInfoFrame::C {
640 ctx: ref mut slot, ..
641 } = self.u
642 {
643 *slot = ctx;
644 }
645 }
646 pub fn set_u_c_old_errfunc(&mut self, old_errfunc: isize) {
648 if let CallInfoFrame::C {
649 old_errfunc: ref mut slot,
650 ..
651 } = self.u
652 {
653 *slot = old_errfunc;
654 }
655 }
656 pub fn set_u2_funcidx(&mut self, idx: i32) {
659 self.u2.value = idx;
660 }
661}
662
663pub trait LuaValueExt {
669 fn base_type(&self) -> lua_types::LuaType;
670 fn to_number_no_strconv(&self) -> Option<f64>;
671 fn to_number_with_strconv(&self) -> Option<f64>;
672 fn to_integer_no_strconv(&self) -> Option<i64>;
673 fn to_integer_with_strconv(&self) -> Option<i64>;
674 fn full_type_tag(&self) -> u8;
675}
676
677impl LuaValueExt for LuaValue {
678 fn base_type(&self) -> lua_types::LuaType {
679 self.type_tag()
680 }
681 fn to_number_no_strconv(&self) -> Option<f64> {
682 match self {
683 LuaValue::Float(f) => Some(*f),
684 LuaValue::Int(i) => Some(*i as f64),
685 _ => None,
686 }
687 }
688 fn to_number_with_strconv(&self) -> Option<f64> {
689 if let Some(n) = self.to_number_no_strconv() {
690 return Some(n);
691 }
692 if let LuaValue::Str(s) = self {
693 let mut tmp = LuaValue::Nil;
694 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
695 if sz == 0 {
696 return None;
697 }
698 return match tmp {
699 LuaValue::Int(i) => Some(i as f64),
700 LuaValue::Float(f) => Some(f),
701 _ => None,
702 };
703 }
704 None
705 }
706 fn to_integer_no_strconv(&self) -> Option<i64> {
707 match self {
708 LuaValue::Int(i) => Some(*i),
709 LuaValue::Float(f) if f.fract() == 0.0 && f.is_finite() => {
710 let min_f = i64::MIN as f64;
714 let max_plus1_f = -(i64::MIN as f64);
715 if *f >= min_f && *f < max_plus1_f {
716 Some(*f as i64)
717 } else {
718 None
719 }
720 }
721 _ => None,
722 }
723 }
724 fn to_integer_with_strconv(&self) -> Option<i64> {
725 if let Some(i) = self.to_integer_no_strconv() {
726 return Some(i);
727 }
728 if let LuaValue::Str(s) = self {
729 let mut tmp = LuaValue::Nil;
730 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
731 if sz == 0 {
732 return None;
733 }
734 return tmp.to_integer_no_strconv();
735 }
736 None
737 }
738 fn full_type_tag(&self) -> u8 {
739 match self {
740 LuaValue::Nil => 0x00,
741 LuaValue::Bool(false) => 0x01,
742 LuaValue::Bool(true) => 0x11,
743 LuaValue::Int(_) => 0x03,
744 LuaValue::Float(_) => 0x13,
745 LuaValue::Str(s) if s.is_short() => 0x04,
746 LuaValue::Str(_) => 0x14,
747 LuaValue::LightUserData(_) => 0x02,
748 LuaValue::Table(_) => 0x05,
749 LuaValue::Function(LuaClosure::Lua(_)) => 0x06,
750 LuaValue::Function(LuaClosure::LightC(_)) => 0x16,
751 LuaValue::Function(LuaClosure::C(_)) => 0x26,
752 LuaValue::UserData(_) => 0x07,
753 LuaValue::Thread(_) => 0x08,
754 }
755 }
756}
757
758pub trait LuaTypeExt {
760 fn type_name(&self) -> &'static [u8];
761}
762
763impl LuaTypeExt for lua_types::LuaType {
764 fn type_name(&self) -> &'static [u8] {
765 use lua_types::LuaType::*;
766 match self {
767 None => b"no value",
768 Nil => b"nil",
769 Boolean => b"boolean",
770 LightUserData => b"userdata",
771 Number => b"number",
772 String => b"string",
773 Table => b"table",
774 Function => b"function",
775 UserData => b"userdata",
776 Thread => b"thread",
777 }
778 }
779}
780
781pub trait StackIdxExt {
785 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32;
786 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32;
787 fn raw(self) -> u32;
788}
789impl StackIdxExt for StackIdx {
790 #[inline(always)]
791 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32 {
792 self.0.saturating_sub(n.into().0 .0)
793 }
794 #[inline(always)]
795 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32 {
796 self.0.wrapping_sub(n.into().0 .0)
797 }
798 #[inline(always)]
799 fn raw(self) -> u32 {
800 self.0
801 }
802}
803
804pub trait LuaTableRefExt {
814 fn metatable(&self) -> Option<GcRef<LuaTable>>;
815 fn has_metatable(&self) -> bool;
816 fn as_ptr(&self) -> *const ();
817 fn get(&self, _k: &LuaValue) -> LuaValue;
818 fn get_int(&self, _k: i64) -> LuaValue;
819 fn get_short_str(&self, _k: &GcRef<LuaString>) -> LuaValue;
820 fn raw_set(&self, _state: &mut LuaState, _k: LuaValue, _v: LuaValue) -> Result<(), LuaError>;
821 fn raw_set_int(&self, _state: &mut LuaState, _k: i64, _v: LuaValue) -> Result<(), LuaError>;
822 fn raw_set_short_str(
823 &self,
824 _state: &mut LuaState,
825 _k: GcRef<LuaString>,
826 _v: LuaValue,
827 ) -> Result<(), LuaError>;
828 fn invalidate_tm_cache(&self);
829 fn resize(&self, _state: &mut LuaState, _na: usize, _nh: usize) -> Result<(), LuaError>;
830 fn next(&self, _k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError>;
831}
832impl LuaTableRefExt for GcRef<LuaTable> {
833 #[inline]
834 fn metatable(&self) -> Option<GcRef<LuaTable>> {
835 (**self).metatable()
836 }
837 #[inline]
838 fn has_metatable(&self) -> bool {
839 (**self).has_metatable()
840 }
841 #[inline]
842 fn as_ptr(&self) -> *const () {
843 GcRef::identity(self) as *const ()
844 }
845 #[inline]
846 fn get(&self, k: &LuaValue) -> LuaValue {
847 (**self).get(k)
848 }
849 #[inline]
850 fn get_int(&self, k: i64) -> LuaValue {
851 (**self).get_int(k)
852 }
853 #[inline]
854 fn get_short_str(&self, k: &GcRef<LuaString>) -> LuaValue {
855 (**self).get_short_str(k)
856 }
857 #[inline(always)]
860 fn raw_set(&self, state: &mut LuaState, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
861 match k {
862 LuaValue::Int(i) => return self.raw_set_int(state, i, v),
863 LuaValue::Str(s) if s.is_short() => return self.raw_set_short_str(state, s, v),
864 k => {
865 let before = (**self).buffer_bytes();
866 let result = (**self).try_raw_set(k, v);
867 if result.is_ok() {
868 account_table_buffer_delta(self, before);
869 }
870 result
871 }
872 }
873 }
874 #[inline(always)]
875 fn raw_set_int(&self, _state: &mut LuaState, k: i64, v: LuaValue) -> Result<(), LuaError> {
876 match (**self).try_update_int(k, v) {
877 Ok(()) => Ok(()),
878 Err(v) => {
879 let before = (**self).buffer_bytes();
880 let result = (**self).try_raw_set_int(k, v);
881 if result.is_ok() {
882 account_table_buffer_delta(self, before);
883 }
884 result
885 }
886 }
887 }
888 #[inline(always)]
889 fn raw_set_short_str(
890 &self,
891 _state: &mut LuaState,
892 k: GcRef<LuaString>,
893 v: LuaValue,
894 ) -> Result<(), LuaError> {
895 match (**self).try_update_short_str(&k, v) {
896 Ok(()) => Ok(()),
897 Err(v) => {
898 let before = (**self).buffer_bytes();
899 let result = (**self).try_raw_set(LuaValue::Str(k), v);
900 if result.is_ok() {
901 account_table_buffer_delta(self, before);
902 }
903 result
904 }
905 }
906 }
907 fn invalidate_tm_cache(&self) {}
908 fn resize(&self, _state: &mut LuaState, na: usize, nh: usize) -> Result<(), LuaError> {
909 let before = (**self).buffer_bytes();
910 let na32 = na.min(u32::MAX as usize) as u32;
911 let nh32 = nh.min(u32::MAX as usize) as u32;
912 let result = (**self).resize(na32, nh32);
913 if result.is_ok() {
914 account_table_buffer_delta(self, before);
915 }
916 result
917 }
918 fn next(&self, k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError> {
919 (**self).try_next_pair(&k)
920 }
921}
922
923#[inline]
924fn account_table_buffer_delta(t: &GcRef<LuaTable>, before: usize) {
925 let after = (**t).buffer_bytes();
926 if after > before {
927 t.account_buffer((after - before) as isize);
928 } else if before > after {
929 t.account_buffer(-((before - after) as isize));
930 }
931}
932
933pub trait LuaUserDataRefExt {
934 fn metatable(&self) -> Option<GcRef<LuaTable>>;
935 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>);
936 fn as_ptr(&self) -> *const ();
937 fn len(&self) -> usize;
938}
939impl LuaUserDataRefExt for GcRef<LuaUserData> {
940 fn metatable(&self) -> Option<GcRef<LuaTable>> {
941 (**self).metatable()
942 }
943 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>) {
944 (**self).set_metatable(mt);
945 }
946 fn as_ptr(&self) -> *const () {
947 GcRef::identity(self) as *const ()
948 }
949 fn len(&self) -> usize {
950 self.0.data.len()
951 }
952}
953
954pub trait LuaStringRefExt {
955 fn is_white(&self) -> bool;
956 fn hash(&self) -> u32;
957 fn as_gc_ref(&self) -> GcRef<LuaString>;
958}
959impl LuaStringRefExt for GcRef<LuaString> {
960 fn is_white(&self) -> bool {
961 false
962 }
963 fn hash(&self) -> u32 {
964 self.0.hash()
965 }
966 fn as_gc_ref(&self) -> GcRef<LuaString> {
967 self.clone()
968 }
969}
970
971pub trait LuaLClosureRefExt {
972 fn proto(&self) -> &GcRef<LuaProto>;
973 fn nupvalues(&self) -> usize;
974}
975impl LuaLClosureRefExt for GcRef<lua_types::closure::LuaLClosure> {
976 fn proto(&self) -> &GcRef<LuaProto> {
977 &self.0.proto
978 }
979 fn nupvalues(&self) -> usize {
980 self.0.upvals.len()
981 }
982}
983
984pub trait LuaClosureExt {
986 fn nupvalues(&self) -> usize;
987}
988impl LuaClosureExt for LuaClosure {
989 fn nupvalues(&self) -> usize {
990 match self {
991 LuaClosure::Lua(l) => l.0.upvals.len(),
992 LuaClosure::C(c) => c.0.upvalues.borrow().len(),
993 LuaClosure::LightC(_) => 0,
994 }
995 }
996}
997
998pub trait LuaProtoExt {
1000 fn source_bytes(&self) -> &[u8];
1001 fn source_string(&self) -> Option<&GcRef<LuaString>>;
1002}
1003impl LuaProtoExt for LuaProto {
1004 fn source_bytes(&self) -> &[u8] {
1005 match &self.source {
1006 Some(s) => s.0.as_bytes(),
1007 None => &[],
1008 }
1009 }
1010 fn source_string(&self) -> Option<&GcRef<LuaString>> {
1011 self.source.as_ref()
1012 }
1013}
1014
1015pub trait Collectable: std::fmt::Debug {}
1022
1023impl std::fmt::Debug for LuaState {
1024 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1025 write!(f, "LuaState")
1026 }
1027}
1028impl Collectable for LuaState {}
1029
1030pub type ParserHook = fn(
1039 state: &mut LuaState,
1040 source: &[u8],
1041 name: &[u8],
1042 firstchar: i32,
1043) -> Result<GcRef<lua_types::closure::LuaLClosure>, LuaError>;
1044
1045pub type FileLoaderHook = fn(filename: &[u8]) -> Result<Vec<u8>, LuaError>;
1053
1054pub type FileOpenHook =
1065 fn(filename: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
1066
1067pub type OutputHook = fn(bytes: &[u8]) -> std::io::Result<()>;
1075
1076pub type InputHook = fn(buf: &mut [u8]) -> std::io::Result<usize>;
1079
1080pub type EnvHook = fn(name: &[u8]) -> Option<Vec<u8>>;
1086
1087pub type UnixTimeHook = fn() -> i64;
1089
1090pub type CpuClockHook = fn() -> f64;
1098
1099pub type LocalOffsetHook = fn(timestamp: i64) -> i64;
1111
1112pub type EntropyHook = fn() -> u64;
1116
1117pub type TempNameHook = fn() -> Result<Vec<u8>, LuaError>;
1122
1123pub type PopenHook =
1134 fn(cmd: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
1135
1136pub type FileRemoveHook = fn(filename: &[u8]) -> Result<(), LuaError>;
1142
1143pub type FileRenameHook = fn(from: &[u8], to: &[u8]) -> Result<(), LuaError>;
1149
1150#[derive(Clone, Copy, Debug)]
1156pub enum OsExecuteReason {
1157 Exit,
1159 Signal,
1161}
1162
1163#[derive(Debug)]
1166pub struct OsExecuteResult {
1167 pub success: bool,
1169 pub reason: OsExecuteReason,
1171 pub code: i32,
1173}
1174
1175pub type OsExecuteHook = fn(cmd: &[u8]) -> Result<OsExecuteResult, LuaError>;
1182
1183#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1190pub struct DynLibId(pub u64);
1191
1192pub enum DynamicSymbol {
1200 RustNative(LuaCFunction),
1203 LuaCAbi(*const ()),
1209 Unsupported { reason: Vec<u8> },
1212}
1213
1214pub type DynLibLoadHook =
1225 fn(state: &mut LuaState, path: &[u8], see_global: bool) -> Result<DynLibId, LuaError>;
1226
1227pub type DynLibSymbolHook =
1235 fn(state: &mut LuaState, handle: DynLibId, symbol: &[u8]) -> Result<DynamicSymbol, LuaError>;
1236
1237pub type DynLibUnloadHook = fn(handle: DynLibId);
1245
1246pub struct ThreadRegistryEntry {
1252 pub state: Rc<RefCell<LuaState>>,
1257 pub value: GcRef<lua_types::value::LuaThread>,
1260}
1261
1262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1267pub struct ExternalRootKey {
1268 index: usize,
1269 generation: u64,
1270}
1271
1272#[derive(Debug)]
1273struct ExternalRootSlot {
1274 value: Option<LuaValue>,
1275 generation: u64,
1276}
1277
1278#[derive(Debug, Default)]
1284pub struct ExternalRootSet {
1285 slots: Vec<ExternalRootSlot>,
1286 free: Vec<usize>,
1287 live: usize,
1288}
1289
1290impl ExternalRootSet {
1291 pub fn insert(&mut self, value: LuaValue) -> ExternalRootKey {
1292 if let Some(index) = self.free.pop() {
1293 let slot = &mut self.slots[index];
1294 debug_assert!(slot.value.is_none(), "free external-root slot is occupied");
1295 slot.generation = slot.generation.wrapping_add(1).max(1);
1296 slot.value = Some(value);
1297 self.live += 1;
1298 ExternalRootKey {
1299 index,
1300 generation: slot.generation,
1301 }
1302 } else {
1303 let index = self.slots.len();
1304 self.slots.push(ExternalRootSlot {
1305 value: Some(value),
1306 generation: 1,
1307 });
1308 self.live += 1;
1309 ExternalRootKey {
1310 index,
1311 generation: 1,
1312 }
1313 }
1314 }
1315
1316 pub fn get(&self, key: ExternalRootKey) -> Option<&LuaValue> {
1317 let slot = self.slots.get(key.index)?;
1318 if slot.generation == key.generation {
1319 slot.value.as_ref()
1320 } else {
1321 None
1322 }
1323 }
1324
1325 pub fn replace(&mut self, key: ExternalRootKey, value: LuaValue) -> Option<LuaValue> {
1326 let slot = self.slots.get_mut(key.index)?;
1327 if slot.generation != key.generation || slot.value.is_none() {
1328 return None;
1329 }
1330 slot.value.replace(value)
1331 }
1332
1333 pub fn remove(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
1334 let slot = self.slots.get_mut(key.index)?;
1335 if slot.generation != key.generation {
1336 return None;
1337 }
1338 let old = slot.value.take()?;
1339 self.free.push(key.index);
1340 self.live -= 1;
1341 Some(old)
1342 }
1343
1344 pub fn iter_values(&self) -> impl Iterator<Item = &LuaValue> {
1345 self.slots.iter().filter_map(|slot| slot.value.as_ref())
1346 }
1347
1348 pub fn len(&self) -> usize {
1349 self.live
1350 }
1351
1352 pub fn is_empty(&self) -> bool {
1353 self.live == 0
1354 }
1355
1356 pub fn vacant_len(&self) -> usize {
1357 self.free.len()
1358 }
1359}
1360
1361pub struct GlobalState {
1367 pub parser_hook: Option<ParserHook>,
1373
1374 pub cli_argv: Option<Vec<Vec<u8>>>,
1381
1382 pub cli_preload: Option<fn(&mut LuaState) -> Result<(), LuaError>>,
1386
1387 pub lua_version: lua_types::LuaVersion,
1394
1395 pub file_loader_hook: Option<FileLoaderHook>,
1400
1401 pub file_open_hook: Option<FileOpenHook>,
1406
1407 pub stdout_hook: Option<OutputHook>,
1411
1412 pub stderr_hook: Option<OutputHook>,
1414
1415 pub stdin_hook: Option<InputHook>,
1418
1419 pub env_hook: Option<EnvHook>,
1421
1422 pub unix_time_hook: Option<UnixTimeHook>,
1425
1426 pub cpu_clock_hook: Option<CpuClockHook>,
1429
1430 pub local_offset_hook: Option<LocalOffsetHook>,
1435
1436 pub entropy_hook: Option<EntropyHook>,
1439
1440 pub temp_name_hook: Option<TempNameHook>,
1442
1443 pub popen_hook: Option<PopenHook>,
1448
1449 pub file_remove_hook: Option<FileRemoveHook>,
1452
1453 pub file_rename_hook: Option<FileRenameHook>,
1456
1457 pub os_execute_hook: Option<OsExecuteHook>,
1461
1462 pub dynlib_load_hook: Option<DynLibLoadHook>,
1467
1468 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
1472
1473 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
1477
1478 pub sandbox: SandboxLimits,
1481
1482 pub gc_debt: isize,
1484
1485 pub gc_estimate: usize,
1486
1487 pub lastatomic: usize,
1489
1490 pub strt: StringPool,
1492
1493 pub l_registry: LuaValue,
1495
1496 pub external_roots: ExternalRootSet,
1499
1500 pub globals: LuaValue,
1507 pub loaded: LuaValue,
1508
1509 pub nilvalue: LuaValue,
1513
1514 pub seed: u32,
1516
1517 pub currentwhite: u8,
1519
1520 pub gcstate: u8,
1521
1522 pub gckind: u8,
1523
1524 pub gcstopem: bool,
1525
1526 pub genminormul: u8,
1528
1529 pub genmajormul: u8,
1530
1531 pub gcstp: u8,
1532
1533 pub gcemergency: bool,
1534
1535 pub gcpause: u8,
1537
1538 pub gcstepmul: u8,
1540
1541 pub gcstepsize: u8,
1542
1543 pub gc55_params: [i64; 6],
1552
1553 pub sweepgc_cursor: usize,
1558
1559 pub weak_tables_registry: lua_gc::WeakRegistry<WeakTableEntry>,
1568
1569 pub finalizers: lua_gc::FinalizerRegistry<FinalizerObject>,
1572
1573 pub gc_finalizer_error: Option<LuaValue>,
1584
1585 pub twups: Vec<GcRef<LuaState>>,
1595
1596 pub panic: Option<LuaCFunction>,
1598
1599 pub mainthread: Option<GcRef<LuaState>>,
1602
1603 pub threads: std::collections::HashMap<u64, ThreadRegistryEntry>,
1616
1617 pub main_thread_value: GcRef<lua_types::value::LuaThread>,
1622
1623 pub current_thread_id: u64,
1628
1629 pub closing_thread_id: Option<u64>,
1632
1633 pub main_thread_id: u64,
1636
1637 pub next_thread_id: u64,
1640
1641 pub memerrmsg: GcRef<LuaString>,
1643
1644 pub tmname: Vec<GcRef<LuaString>>,
1647
1648 pub mt: [Option<GcRef<LuaTable>>; LUA_NUMTYPES],
1650
1651 pub strcache: [[GcRef<LuaString>; STRCACHE_M]; STRCACHE_N],
1653
1654 pub interned_lt: InternedStringMap,
1660
1661 pub warnf: Option<Box<dyn FnMut(&[u8], bool)>>,
1663
1664 pub warn_mode: WarnMode,
1669
1670 pub test_warn_enabled: bool,
1675 pub test_warn_on: bool,
1676 pub test_warn_mode: TestWarnMode,
1677 pub test_warn_last_to_cont: bool,
1678 pub test_warn_buffer: Vec<u8>,
1679
1680 pub c_functions: Vec<LuaCallable>,
1685
1686 pub heap: lua_gc::Heap,
1691
1692 pub cross_thread_upvals: std::collections::HashMap<(u64, StackIdx), LuaValue>,
1706
1707 pub suspended_parent_stacks: Vec<Vec<LuaValue>>,
1721
1722 pub suspended_parent_open_upvals: Vec<Vec<GcRef<UpVal>>>,
1728}
1729
1730const SANDBOX_COUNT_MASK: u8 = 1 << 3;
1733
1734pub const SANDBOX_TRIP_NONE: u8 = 0;
1736pub const SANDBOX_TRIP_INSTRUCTIONS: u8 = 1;
1738pub const SANDBOX_TRIP_MEMORY: u8 = 2;
1740
1741#[derive(Default)]
1748pub struct SandboxLimits {
1749 pub interval: std::cell::Cell<i32>,
1751 pub instr_limited: std::cell::Cell<bool>,
1753 pub instr_remaining: std::cell::Cell<u64>,
1755 pub instr_limit: std::cell::Cell<u64>,
1757 pub mem_limit: std::cell::Cell<Option<usize>>,
1759 pub tripped: std::cell::Cell<u8>,
1761 pub aborting: std::cell::Cell<bool>,
1766}
1767
1768impl GlobalState {
1769 pub fn sandbox_active(&self) -> bool {
1771 self.sandbox.interval.get() != 0
1772 }
1773
1774 pub fn total_bytes(&self) -> usize {
1779 self.heap.bytes_used().max(1)
1780 }
1781
1782 pub fn get_thread(&self, id: u64) -> Option<&ThreadRegistryEntry> {
1787 self.threads.get(&id)
1788 }
1789
1790 pub fn thread_value_for(&self, id: u64) -> Option<GcRef<lua_types::value::LuaThread>> {
1794 if id == self.main_thread_id {
1795 Some(self.main_thread_value.clone())
1796 } else {
1797 self.threads.get(&id).map(|e| e.value.clone())
1798 }
1799 }
1800
1801 pub fn is_complete(&self) -> bool {
1808 matches!(self.nilvalue, LuaValue::Nil)
1809 }
1810
1811 pub fn current_white(&self) -> u8 {
1819 self.currentwhite
1820 }
1821
1822 pub fn other_white(&self) -> u8 {
1826 self.currentwhite ^ 0x03
1827 }
1828
1829 pub fn is_gen_mode(&self) -> bool {
1833 self.gckind == GcKind::Generational as u8 || self.lastatomic != 0
1834 }
1835
1836 pub fn gc_running(&self) -> bool {
1840 self.gcstp == 0
1841 }
1842
1843 pub fn keep_invariant(&self) -> bool {
1847 self.heap.gc_state().is_invariant()
1848 }
1849
1850 pub fn is_sweep_phase(&self) -> bool {
1854 self.heap.gc_state().is_sweep()
1855 }
1856
1857 pub fn gc_debt(&self) -> isize {
1859 self.gc_debt
1860 }
1861 pub fn set_gc_debt(&mut self, d: isize) {
1862 self.gc_debt = d;
1863 }
1864 pub fn gc_at_pause(&self) -> bool {
1865 self.heap.gc_state().is_pause()
1866 }
1867 fn get_gc_param(p: u8) -> i32 {
1868 (p as i32) * 4
1869 }
1870 fn set_gc_param_slot(slot: &mut u8, p: i32) {
1871 *slot = (p / 4) as u8;
1872 }
1873 pub fn gc_pause_param(&self) -> i32 {
1874 Self::get_gc_param(self.gcpause)
1875 }
1876 pub fn set_gc_pause_param(&mut self, p: i32) {
1877 Self::set_gc_param_slot(&mut self.gcpause, p);
1878 }
1879 pub fn gc_stepmul_param(&self) -> i32 {
1880 Self::get_gc_param(self.gcstepmul)
1881 }
1882 pub fn set_gc_stepmul_param(&mut self, p: i32) {
1883 Self::set_gc_param_slot(&mut self.gcstepmul, p);
1884 }
1885 pub fn gc_genmajormul_param(&self) -> i32 {
1886 Self::get_gc_param(self.genmajormul)
1887 }
1888 pub fn set_gc_genmajormul(&mut self, p: i32) {
1889 Self::set_gc_param_slot(&mut self.genmajormul, p);
1890 }
1891 pub fn gc55_param(&mut self, idx: usize, value: i64) -> i64 {
1895 let old = self.gc55_params[idx];
1896 if value >= 0 {
1897 self.gc55_params[idx] = value;
1898 }
1899 old
1900 }
1901 pub fn gc_stop_flags(&self) -> u8 {
1902 self.gcstp
1903 }
1904 pub fn set_gc_stop_flags(&mut self, f: u8) {
1905 self.gcstp = f;
1906 }
1907 pub fn stop_gc_internal(&mut self) -> u8 {
1908 let old = self.gcstp;
1909 self.gcstp |= GCSTPGC;
1910 old
1911 }
1912 pub fn set_gc_stop_user(&mut self) {
1913 self.gcstp = GCSTPUSR;
1915 }
1916 pub fn clear_gc_stop(&mut self) {
1917 self.gcstp = 0;
1918 }
1919 pub fn is_gc_running(&self) -> bool {
1920 self.gcstp == 0
1921 }
1922 pub fn is_gc_stopped_internally(&self) -> bool {
1927 (self.gcstp & GCSTPGC) != 0
1928 }
1929
1930 pub fn tm_name<T: TmIndex>(&self, tm: T) -> Option<GcRef<LuaString>> {
1940 self.tmname.get(tm.tm_index()).cloned()
1941 }
1942}
1943
1944pub trait TmIndex: Copy {
1950 fn tm_index(self) -> usize;
1951}
1952impl TmIndex for lua_types::tagmethod::TagMethod {
1953 fn tm_index(self) -> usize {
1954 self as u8 as usize
1955 }
1956}
1957impl TmIndex for crate::tagmethods::TagMethod {
1958 fn tm_index(self) -> usize {
1959 self as u8 as usize
1960 }
1961}
1962impl TmIndex for usize {
1963 fn tm_index(self) -> usize {
1964 self
1965 }
1966}
1967impl TmIndex for u8 {
1968 fn tm_index(self) -> usize {
1969 self as usize
1970 }
1971}
1972
1973use lua_types::tagmethod::TagMethod;
1974
1975pub struct LuaState {
1985 pub status: u8,
1989
1990 pub allowhook: bool,
1992
1993 pub nci: u32,
1995
1996 pub top: StackIdx,
2000
2001 pub stack_last: StackIdx,
2003
2004 pub stack: Vec<StackValue>,
2006
2007 pub ci: CallInfoIdx,
2011
2012 pub call_info: Vec<CallInfo>,
2015
2016 pub openupval: Vec<GcRef<UpVal>>,
2020
2021 pub tbclist: Vec<StackIdx>,
2023
2024 pub(crate) global: Rc<RefCell<GlobalState>>,
2029
2030 pub hook: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>,
2034
2035 pub hookmask: u8,
2037
2038 pub basehookcount: i32,
2040
2041 pub hookcount: i32,
2043
2044 pub errfunc: isize,
2051
2052 pub n_ccalls: u32,
2056
2057 pub oldpc: u32,
2061
2062 pub marked: u8,
2066
2067 pub cached_thread_id: u64,
2083
2084 pub gc_check_needed: bool,
2089}
2090
2091impl LuaState {
2092 pub fn global(&self) -> std::cell::Ref<'_, GlobalState> {
2100 self.global.borrow()
2101 }
2102
2103 pub fn global_mut(&self) -> std::cell::RefMut<'_, GlobalState> {
2107 self.global.borrow_mut()
2108 }
2109
2110 pub fn global_rc(&self) -> Rc<RefCell<GlobalState>> {
2114 Rc::clone(&self.global)
2115 }
2116
2117 pub fn enable_test_warning_handler(&mut self) -> Result<(), LuaError> {
2119 {
2120 let mut g = self.global_mut();
2121 g.test_warn_enabled = true;
2122 g.test_warn_on = false;
2123 g.test_warn_mode = TestWarnMode::Normal;
2124 g.test_warn_last_to_cont = false;
2125 g.test_warn_buffer.clear();
2126 }
2127 self.push(LuaValue::Bool(false));
2128 crate::api::set_global(self, b"_WARN")
2129 }
2130
2131 pub fn c_calls(&self) -> u32 {
2135 self.n_ccalls & 0xffff
2136 }
2137
2138 pub fn inc_nny(&mut self) {
2142 self.n_ccalls += 0x10000;
2143 }
2144
2145 pub fn dec_nny(&mut self) {
2149 self.n_ccalls -= 0x10000;
2150 }
2151
2152 pub fn is_yieldable(&self) -> bool {
2156 (self.n_ccalls & 0xffff0000) == 0
2157 }
2158
2159 pub fn reset_hook_count(&mut self) {
2163 self.hookcount = self.basehookcount;
2164 }
2165
2166 pub fn install_sandbox_limits(
2175 &mut self,
2176 interval: i32,
2177 instr_limit: Option<u64>,
2178 mem_limit: Option<usize>,
2179 ) {
2180 let interval = interval.max(1);
2181 {
2182 let g = self.global();
2183 g.sandbox.interval.set(interval);
2184 g.sandbox.instr_limited.set(instr_limit.is_some());
2185 g.sandbox.instr_remaining.set(instr_limit.unwrap_or(0));
2186 g.sandbox.instr_limit.set(instr_limit.unwrap_or(0));
2187 g.sandbox.mem_limit.set(mem_limit);
2188 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2189 }
2190 self.hookmask |= SANDBOX_COUNT_MASK;
2191 self.basehookcount = interval;
2192 self.hookcount = interval;
2193 crate::debug::arm_traps(self);
2194 }
2195
2196 pub fn sandbox_charge_interval(&self) -> Option<LuaError> {
2201 let interval = self.global().sandbox.interval.get();
2202 self.sandbox_charge(interval as u64)
2203 }
2204
2205 pub fn sandbox_charge(&self, amount: u64) -> Option<LuaError> {
2214 let g = self.global();
2215 if g.sandbox.interval.get() == 0 {
2216 return None;
2217 }
2218 if g.sandbox.instr_limited.get() {
2219 let rem = g.sandbox.instr_remaining.get().saturating_sub(amount);
2220 g.sandbox.instr_remaining.set(rem);
2221 if rem == 0 {
2222 g.sandbox.tripped.set(SANDBOX_TRIP_INSTRUCTIONS);
2223 g.sandbox.aborting.set(true);
2224 return Some(LuaError::runtime(format_args!(
2225 "sandbox: instruction budget exhausted"
2226 )));
2227 }
2228 }
2229 if let Some(limit) = g.sandbox.mem_limit.get() {
2230 if g.total_bytes() > limit {
2231 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2232 g.sandbox.aborting.set(true);
2233 return Some(LuaError::runtime(format_args!(
2234 "sandbox: memory limit exceeded"
2235 )));
2236 }
2237 }
2238 None
2239 }
2240
2241 pub fn sandbox_reserve(&self, additional: usize) -> Option<LuaError> {
2249 let g = self.global();
2250 if g.sandbox.interval.get() == 0 {
2251 return None;
2252 }
2253 if let Some(limit) = g.sandbox.mem_limit.get() {
2254 let projected = g.total_bytes().saturating_add(additional);
2255 if projected > limit {
2256 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2257 g.sandbox.aborting.set(true);
2258 return Some(LuaError::runtime(format_args!(
2259 "sandbox: memory limit exceeded"
2260 )));
2261 }
2262 }
2263 None
2264 }
2265
2266 pub fn sandbox_match_step_limit(&self) -> u64 {
2271 let g = self.global();
2272 if g.sandbox.interval.get() != 0 && g.sandbox.instr_limited.get() {
2273 g.sandbox.instr_remaining.get()
2274 } else {
2275 0
2276 }
2277 }
2278
2279 pub fn sandbox_aborting(&self) -> bool {
2283 self.global().sandbox.aborting.get()
2284 }
2285
2286 pub fn sandbox_instr_limited(&self) -> bool {
2288 self.global().sandbox.instr_limited.get()
2289 }
2290
2291 pub fn sandbox_instr_remaining(&self) -> u64 {
2294 self.global().sandbox.instr_remaining.get()
2295 }
2296
2297 pub fn sandbox_instr_limit(&self) -> u64 {
2299 self.global().sandbox.instr_limit.get()
2300 }
2301
2302 pub fn sandbox_tripped_code(&self) -> u8 {
2304 self.global().sandbox.tripped.get()
2305 }
2306
2307 pub fn sandbox_reset(&self) {
2310 let g = self.global();
2311 if g.sandbox.instr_limited.get() {
2312 g.sandbox.instr_remaining.set(g.sandbox.instr_limit.get());
2313 }
2314 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2315 g.sandbox.aborting.set(false);
2316 }
2317
2318 pub fn stack_size(&self) -> usize {
2322 self.stack_last.0 as usize
2323 }
2324
2325 #[inline(always)]
2329 pub fn push(&mut self, val: LuaValue) {
2330 let top = self.top.0 as usize;
2331 if top < self.stack.len() {
2332 self.stack[top] = StackValue { val, tbc_delta: 0 };
2333 } else {
2334 self.stack.push(StackValue { val, tbc_delta: 0 });
2335 }
2336 self.top = StackIdx(self.top.0 + 1);
2337 }
2338
2339 #[inline(always)]
2342 pub fn pop(&mut self) -> LuaValue {
2343 if self.top.0 == 0 {
2344 return LuaValue::Nil;
2345 }
2346 self.top = StackIdx(self.top.0 - 1);
2347 self.stack[self.top.0 as usize].val.clone()
2348 }
2349
2350 #[inline(always)]
2354 pub fn stack_val(&self, idx: StackIdx) -> &LuaValue {
2355 &self.stack[idx.0 as usize].val
2356 }
2357
2358 #[inline(always)]
2360 pub fn set_stack_val(&mut self, idx: StackIdx, val: LuaValue) {
2361 self.stack[idx.0 as usize].val = val;
2362 }
2363
2364 pub fn gc(&mut self) -> GcHandle<'_> {
2371 GcHandle { _state: self }
2372 }
2373
2374 pub fn external_root_value(&mut self, value: LuaValue) -> ExternalRootKey {
2376 self.global_mut().external_roots.insert(value)
2377 }
2378
2379 pub fn external_rooted_value(&self, key: ExternalRootKey) -> Option<LuaValue> {
2381 self.global().external_roots.get(key).cloned()
2382 }
2383
2384 pub fn external_replace_root(
2386 &mut self,
2387 key: ExternalRootKey,
2388 value: LuaValue,
2389 ) -> Option<LuaValue> {
2390 self.global_mut().external_roots.replace(key, value)
2391 }
2392
2393 pub fn external_unroot_value(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
2395 self.global_mut().external_roots.remove(key)
2396 }
2397
2398 pub fn try_external_unroot_value(
2401 &mut self,
2402 key: ExternalRootKey,
2403 ) -> std::result::Result<Option<LuaValue>, std::cell::BorrowMutError> {
2404 self.global
2405 .try_borrow_mut()
2406 .map(|mut global| global.external_roots.remove(key))
2407 }
2408
2409 pub fn new_table(&mut self) -> GcRef<LuaTable> {
2413 self.mark_gc_check_needed();
2415 GcRef::new(LuaTable::placeholder())
2416 }
2417
2418 pub fn new_table_with_sizes(
2423 &mut self,
2424 array_size: u32,
2425 hash_size: u32,
2426 ) -> Result<GcRef<LuaTable>, LuaError> {
2427 self.mark_gc_check_needed();
2428 let t = GcRef::new(LuaTable::placeholder());
2429 self.table_resize(&t, array_size as usize, hash_size as usize)?;
2430 Ok(t)
2431 }
2432
2433 pub fn intern_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2444 if bytes.len() <= crate::string::MAX_SHORT_LEN {
2445 let mut inserted = false;
2446 let interned = {
2447 let key = bytes.to_vec().into_boxed_slice();
2448 let mut global = self.global_mut();
2449 match global.interned_lt.entry(key) {
2450 Entry::Occupied(existing) => existing.get().clone(),
2451 Entry::Vacant(vacant) => {
2452 let new_ref = GcRef::new(LuaString::from_bytes(vacant.key().to_vec()));
2453 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2454 vacant.insert(new_ref.clone());
2455 inserted = true;
2456 new_ref
2457 }
2458 }
2459 };
2460 if inserted {
2461 self.mark_gc_check_needed();
2462 }
2463 Ok(interned)
2464 } else {
2465 self.mark_gc_check_needed();
2466 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2467 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2468 Ok(new_ref)
2469 }
2470 }
2471
2472 #[inline(always)]
2474 pub fn top_idx(&self) -> StackIdx {
2475 self.top
2476 }
2477}
2478
2479impl LuaState {
2488 #[inline(always)]
2489 pub fn get_at(&self, idx: impl Into<StackIdxConv>) -> LuaValue {
2490 let i: StackIdx = idx.into().0;
2491 match self.stack.get(i.0 as usize) {
2492 Some(slot) => slot.val.clone(),
2493 None => LuaValue::Nil,
2494 }
2495 }
2496 #[inline(always)]
2497 pub fn set_at(&mut self, idx: impl Into<StackIdxConv>, v: LuaValue) {
2498 let i: StackIdx = idx.into().0;
2499 self.stack[i.0 as usize].val = v;
2500 }
2501
2502 pub fn clear_stack_range(&mut self, start: StackIdx, end: StackIdx) {
2508 if end.0 <= start.0 {
2509 return;
2510 }
2511 let end_u = end.0 as usize;
2512 if end_u > self.stack.len() {
2513 self.stack.resize_with(end_u, StackValue::default);
2514 }
2515 for i in start.0..end.0 {
2516 self.stack[i as usize].val = LuaValue::Nil;
2517 self.stack[i as usize].tbc_delta = 0;
2518 }
2519 }
2520 #[inline(always)]
2528 pub fn get_int_at(&self, idx: impl Into<StackIdxConv>) -> Option<i64> {
2529 let i: StackIdx = idx.into().0;
2530 match self.stack.get(i.0 as usize) {
2531 Some(slot) => match &slot.val {
2532 LuaValue::Int(v) => Some(*v),
2533 _ => None,
2534 },
2535 None => None,
2536 }
2537 }
2538 #[inline(always)]
2545 pub fn get_int_pair_at(
2546 &self,
2547 rb: impl Into<StackIdxConv>,
2548 rc: impl Into<StackIdxConv>,
2549 ) -> Option<(i64, i64)> {
2550 let rb: StackIdx = rb.into().0;
2551 let rc: StackIdx = rc.into().0;
2552 match (self.stack[rb.0 as usize].val, self.stack[rc.0 as usize].val) {
2553 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib, ic)),
2554 _ => None,
2555 }
2556 }
2557 #[inline(always)]
2562 pub fn get_num_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2563 let i: StackIdx = idx.into().0;
2564 match self.stack.get(i.0 as usize) {
2565 Some(slot) => match &slot.val {
2566 LuaValue::Float(f) => Some(*f),
2567 LuaValue::Int(v) => Some(*v as f64),
2568 _ => None,
2569 },
2570 None => None,
2571 }
2572 }
2573 #[inline(always)]
2578 pub fn get_float_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2579 let i: StackIdx = idx.into().0;
2580 match self.stack.get(i.0 as usize) {
2581 Some(slot) => match &slot.val {
2582 LuaValue::Float(f) => Some(*f),
2583 _ => None,
2584 },
2585 None => None,
2586 }
2587 }
2588 #[inline(always)]
2593 pub fn get_num_pair_at(
2594 &self,
2595 rb: impl Into<StackIdxConv>,
2596 rc: impl Into<StackIdxConv>,
2597 ) -> Option<(f64, f64)> {
2598 let rb: StackIdx = rb.into().0;
2599 let rc: StackIdx = rc.into().0;
2600 match (self.stack[rb.0 as usize].val, self.stack[rc.0 as usize].val) {
2601 (LuaValue::Float(nb), LuaValue::Float(nc)) => Some((nb, nc)),
2602 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib as f64, ic as f64)),
2603 (LuaValue::Int(ib), LuaValue::Float(nc)) => Some((ib as f64, nc)),
2604 (LuaValue::Float(nb), LuaValue::Int(ic)) => Some((nb, ic as f64)),
2605 _ => None,
2606 }
2607 }
2608 #[inline(always)]
2621 pub fn set_top(&mut self, idx: impl Into<StackIdxConv>) {
2622 let new_top: StackIdx = idx.into().0;
2623 let new_top_u = new_top.0 as usize;
2624 if new_top_u > self.stack.len() {
2625 self.stack.resize_with(new_top_u, StackValue::default);
2626 }
2627 self.top = new_top;
2628 }
2629 #[inline(always)]
2635 pub fn set_top_idx(&mut self, idx: impl Into<StackIdxConv>) {
2636 let new_top: StackIdx = idx.into().0;
2637 self.top = new_top;
2638 }
2639 #[inline(always)]
2642 pub fn dec_top(&mut self) {
2643 if self.top.0 > 0 {
2644 self.top = StackIdx(self.top.0 - 1);
2645 }
2646 }
2647 #[inline(always)]
2648 pub fn pop_n(&mut self, n: usize) {
2649 let cur = self.top.0 as usize;
2650 let new = cur.saturating_sub(n);
2651 self.top = StackIdx(new as u32);
2652 }
2653 #[inline(always)]
2656 pub fn peek_at(&mut self, idx: impl Into<StackIdxConv>) -> LuaValue {
2657 let i: StackIdx = idx.into().0;
2658 match self.stack.get(i.0 as usize) {
2659 Some(slot) => slot.val.clone(),
2660 None => LuaValue::Nil,
2661 }
2662 }
2663 #[inline(always)]
2667 pub fn peek_top(&mut self) -> LuaValue {
2668 if self.top.0 == 0 {
2669 return LuaValue::Nil;
2670 }
2671 self.stack[(self.top.0 - 1) as usize].val.clone()
2672 }
2673 pub fn peek_string_at_top(&mut self) -> GcRef<LuaString> {
2678 match self.peek_top() {
2679 LuaValue::Str(s) => s,
2680 _ => panic!("peek_string_at_top: top of stack is not a string"),
2681 }
2682 }
2683 pub fn stack_at(&mut self, idx: impl Into<StackIdxConv>) -> &mut LuaValue {
2686 let i: StackIdx = idx.into().0;
2687 &mut self.stack[i.0 as usize].val
2688 }
2689 pub fn stack_set_nil(&mut self, idx: impl Into<StackIdxConv>) {
2692 let i: StackIdx = idx.into().0;
2693 let slot = i.0 as usize;
2694 if slot < self.stack.len() {
2695 self.stack[slot].val = LuaValue::Nil;
2696 }
2697 }
2698 pub fn stack_resize(&mut self, size: usize) -> Result<(), LuaError> {
2706 self.stack.resize_with(size, StackValue::default);
2707 Ok(())
2708 }
2709 pub fn stack_available(&mut self) -> usize {
2710 (self.stack_last.0 as usize).saturating_sub(self.top.0 as usize)
2711 }
2712 pub fn check_stack(&mut self, n: i32) -> Result<(), LuaError> {
2713 let free = (self.stack_last.0 as i32) - (self.top.0 as i32);
2714 if free <= n {
2715 self.grow_stack(n, true)?;
2716 }
2717 Ok(())
2718 }
2719 pub fn grow_stack(&mut self, n: i32, raise_error: bool) -> Result<(), LuaError> {
2728 crate::do_::grow_stack(self, n, raise_error).map(|_| ())
2729 }
2730
2731 #[inline(always)]
2732 pub fn get_ci(&self, idx: CallInfoIdx) -> &CallInfo {
2733 &self.call_info[idx.as_usize()]
2734 }
2735 #[inline(always)]
2736 pub fn get_ci_mut(&mut self, idx: CallInfoIdx) -> &mut CallInfo {
2737 &mut self.call_info[idx.as_usize()]
2738 }
2739 #[inline(always)]
2740 pub fn current_call_info(&self) -> &CallInfo {
2741 &self.call_info[self.ci.as_usize()]
2742 }
2743 #[inline(always)]
2744 pub fn current_call_info_mut(&mut self) -> &mut CallInfo {
2745 let i = self.ci.as_usize();
2746 &mut self.call_info[i]
2747 }
2748 #[inline(always)]
2749 pub fn current_ci_idx(&self) -> CallInfoIdx {
2750 self.ci
2751 }
2752 pub fn call_stack_mut(&mut self) -> &mut Vec<CallInfo> {
2753 &mut self.call_info
2754 }
2755 #[inline(always)]
2756 pub fn next_ci(&mut self) -> Result<CallInfoIdx, LuaError> {
2757 match self.call_info[self.ci.as_usize()].next {
2758 Some(idx) => Ok(idx),
2759 None => Ok(extend_ci(self)),
2760 }
2761 }
2762 #[inline(always)]
2763 pub fn prev_ci(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> {
2764 self.call_info[idx.as_usize()].previous
2765 }
2766 pub fn get_prev_ci(&self, idx: CallInfoIdx) -> Option<&CallInfo> {
2767 self.call_info[idx.as_usize()]
2768 .previous
2769 .map(|p| &self.call_info[p.as_usize()])
2770 }
2771 #[inline(always)]
2772 pub fn is_base_ci(&self, idx: CallInfoIdx) -> bool {
2773 idx.as_usize() == 0
2774 }
2775 #[inline(always)]
2776 pub fn is_current_ci(&self, idx: CallInfoIdx) -> bool {
2777 idx == self.ci
2778 }
2779 pub fn ci_next_func(&self, idx: CallInfoIdx) -> StackIdx {
2780 let next = self.call_info[idx.as_usize()]
2781 .next
2782 .expect("ci_next_func: no next CallInfo");
2783 self.call_info[next.as_usize()].func
2784 }
2785 #[inline(always)]
2786 pub fn ci_top(&self, idx: CallInfoIdx) -> StackIdx {
2787 self.call_info[idx.as_usize()].top
2788 }
2789 #[inline(always)]
2790 pub fn ci_trap(&mut self, idx: CallInfoIdx) -> bool {
2791 if let CallInfoFrame::Lua { trap, .. } = self.call_info[idx.as_usize()].u {
2792 trap
2793 } else {
2794 false
2795 }
2796 }
2797 #[inline(always)]
2798 pub fn ci_savedpc(&self, idx: CallInfoIdx) -> u32 {
2799 self.call_info[idx.as_usize()].saved_pc()
2800 }
2801 #[inline(always)]
2802 pub fn set_ci_savedpc(&mut self, idx: CallInfoIdx, pc: u32) {
2803 self.call_info[idx.as_usize()].set_saved_pc(pc);
2804 }
2805 #[inline(always)]
2806 pub fn set_ci_previous(&mut self, idx: CallInfoIdx) {
2807 self.ci = self.call_info[idx.as_usize()]
2808 .previous
2809 .expect("set_ci_previous: returning frame has no previous CallInfo");
2810 }
2811 #[inline(always)]
2812 pub fn ci_previous(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> {
2813 self.call_info[idx.as_usize()].previous
2814 }
2815 #[inline(always)]
2816 pub fn ci_adjust_func(&mut self, idx: CallInfoIdx, delta: i32) {
2817 let ci = &mut self.call_info[idx.as_usize()];
2818 ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
2819 }
2820 #[inline(always)]
2821 pub fn ci_base(&self, idx: CallInfoIdx) -> StackIdx {
2822 self.call_info[idx.as_usize()].func + 1
2823 }
2824 #[inline(always)]
2825 pub fn ci_is_fresh(&self, idx: CallInfoIdx) -> bool {
2826 (self.call_info[idx.as_usize()].callstatus & CIST_FRESH) != 0
2827 }
2828 #[inline(always)]
2829 pub fn ci_lua_closure(
2830 &self,
2831 idx: CallInfoIdx,
2832 ) -> Option<GcRef<lua_types::closure::LuaLClosure>> {
2833 let func_idx = self.call_info[idx.as_usize()].func;
2834 match self.stack.get(func_idx.0 as usize).map(|slot| slot.val) {
2835 Some(LuaValue::Function(lua_types::closure::LuaClosure::Lua(cl))) => Some(cl),
2836 _ => None,
2837 }
2838 }
2839 #[inline(always)]
2840 pub fn ci_nextraargs(&self, idx: CallInfoIdx) -> i32 {
2841 self.call_info[idx.as_usize()].nextra_args()
2842 }
2843 #[inline(always)]
2844 pub fn ci_nres(&self, idx: CallInfoIdx) -> i32 {
2845 self.call_info[idx.as_usize()].u2.value
2846 }
2847 #[inline(always)]
2848 pub fn ci_nres_set(&mut self, idx: CallInfoIdx, n: i32) {
2849 self.call_info[idx.as_usize()].u2.value = n;
2850 }
2851 #[inline(always)]
2852 pub fn ci_nresults(&self, idx: CallInfoIdx) -> i32 {
2853 self.call_info[idx.as_usize()].nresults as i32
2854 }
2855 pub fn ci_prev_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2856 let pc = self.call_info[idx.as_usize()].saved_pc();
2857 let cl = self
2858 .ci_lua_closure(idx)
2859 .expect("ci_prev_instruction: CallInfo does not hold a Lua closure");
2860 cl.proto.code[(pc - 1) as usize]
2861 }
2862 pub fn ci_prev2_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2863 let pc = self.call_info[idx.as_usize()].saved_pc();
2864 let cl = self
2865 .ci_lua_closure(idx)
2866 .expect("ci_prev2_instruction: CallInfo does not hold a Lua closure");
2867 cl.proto.code[(pc - 2) as usize]
2868 }
2869 pub fn ci_skip_next_instruction(&mut self, idx: CallInfoIdx) {
2870 let pc = self.call_info[idx.as_usize()].saved_pc();
2871 self.call_info[idx.as_usize()].set_saved_pc(pc + 1);
2872 }
2873 pub fn ci_step_pc_back(&mut self, idx: CallInfoIdx) {
2874 let pc = self.call_info[idx.as_usize()].saved_pc();
2875 self.call_info[idx.as_usize()].set_saved_pc(pc - 1);
2876 }
2877 pub fn get_ci_pcrel(&mut self, idx: CallInfoIdx) -> u32 {
2878 self.call_info[idx.as_usize()].saved_pc().saturating_sub(1)
2879 }
2880 pub fn get_ci_u2_funcidx(&mut self, idx: CallInfoIdx) -> i32 {
2881 self.call_info[idx.as_usize()].u2.value
2882 }
2883 pub fn get_ci_u2_nres(&mut self, idx: CallInfoIdx) -> i32 {
2884 self.call_info[idx.as_usize()].u2.value
2885 }
2886 pub fn get_ci_u2_nyield(&mut self, idx: CallInfoIdx) -> i32 {
2887 self.call_info[idx.as_usize()].u2.value
2888 }
2889 pub fn get_ci_vararg_info(&mut self, idx: CallInfoIdx) -> (bool, i32, i32) {
2890 let nextraargs = self.call_info[idx.as_usize()].nextra_args();
2891 match self.ci_lua_closure(idx) {
2892 Some(cl) => (cl.proto.is_vararg, nextraargs, cl.proto.numparams as i32),
2893 None => (false, nextraargs, 0),
2894 }
2895 }
2896 pub fn get_ci_lua_proto_numparams(&mut self, idx: CallInfoIdx) -> u8 {
2897 self.ci_lua_closure(idx)
2898 .map(|cl| cl.proto.numparams)
2899 .unwrap_or(0)
2900 }
2901 pub fn set_ci_u2_nres(&mut self, idx: CallInfoIdx, n: i32) {
2902 self.call_info[idx.as_usize()].u2.value = n;
2903 }
2904 pub fn set_ci_u2_nyield(&mut self, idx: CallInfoIdx, n: i32) {
2905 self.call_info[idx.as_usize()].u2.value = n;
2906 }
2907 pub fn set_ci_transfer_info(&mut self, idx: CallInfoIdx, ftransfer: u16, ntransfer: u16) {
2908 let ci = &mut self.call_info[idx.as_usize()];
2909 ci.u2.ftransfer = ftransfer;
2910 ci.u2.ntransfer = ntransfer;
2911 }
2912 pub fn shrink_ci(&mut self) {
2913 shrink_ci(self)
2914 }
2915 pub fn check_c_stack(&mut self) -> Result<(), LuaError> {
2916 check_c_stack(self)
2917 }
2918
2919 pub fn status(&mut self) -> LuaStatus {
2920 LuaStatus::from_raw(self.status as i32)
2921 }
2922 pub fn errfunc(&mut self) -> isize {
2923 self.errfunc
2924 }
2925 pub fn old_pc(&mut self) -> u32 {
2926 self.oldpc
2927 }
2928 pub fn set_old_pc(&mut self, pc: u32) {
2929 self.oldpc = pc;
2930 }
2931 pub fn set_oldpc(&mut self, pc: u32) {
2932 self.oldpc = pc;
2933 }
2934 pub fn _hook_call_noargs(&mut self) {}
2935 pub fn hook(&self) -> Option<&Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>> {
2936 self.hook.as_ref()
2937 }
2938 pub fn has_hook(&mut self) -> bool {
2939 self.hook.is_some()
2940 }
2941 pub fn hook_count(&mut self) -> i32 {
2942 self.hookcount
2943 }
2944 pub fn set_hook_count(&mut self, n: i32) {
2945 self.hookcount = n;
2946 }
2947 pub fn hook_mask(&self) -> u8 {
2948 self.hookmask
2949 }
2950 pub fn set_hook_mask(&mut self, m: u8) {
2951 self.hookmask = m;
2952 }
2953 pub fn base_hook_count(&self) -> i32 {
2954 self.basehookcount
2955 }
2956 pub fn set_base_hook_count(&mut self, n: i32) {
2957 self.basehookcount = n;
2958 }
2959 pub fn set_hook(&mut self, h: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>) {
2960 self.hook = h;
2961 }
2962 pub fn call_hook_event(&mut self, event: i32, line: i32) -> Result<(), LuaError> {
2963 crate::do_::hook(self, event, line, 0, 0)
2964 }
2965
2966 pub fn registry_value(&self) -> LuaValue {
2967 self.global().l_registry.clone()
2968 }
2969 pub fn registry_get(&self, key: usize) -> LuaValue {
2970 let reg = self.global().l_registry.clone();
2971 match reg {
2972 LuaValue::Table(t) => t.get(&LuaValue::Int(key as i64)),
2973 _ => LuaValue::Nil,
2974 }
2975 }
2976
2977 pub fn new_string(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2978 self.intern_or_create_str(bytes)
2979 }
2980
2981 pub fn new_proto(&mut self) -> GcRef<LuaProto> {
2993 self.mark_gc_check_needed();
2994 GcRef::new(LuaProto::placeholder())
2995 }
2996
2997 pub fn new_lclosure(&mut self, proto: GcRef<LuaProto>, nupvals: usize) -> GcRef<LuaClosureLua> {
2999 self.mark_gc_check_needed();
3000 let mut upvals = Vec::with_capacity(nupvals);
3001 for _ in 0..nupvals {
3002 upvals.push(std::cell::Cell::new(self.new_upval_closed(LuaValue::Nil)));
3003 }
3004 let closure = GcRef::new(LuaClosureLua { proto, upvals });
3005 closure.account_buffer(closure.buffer_bytes() as isize);
3006 closure
3007 }
3008
3009 pub fn new_upval_closed(&mut self, v: LuaValue) -> GcRef<UpVal> {
3011 self.mark_gc_check_needed();
3012 GcRef::new(UpVal::closed(v))
3013 }
3014
3015 pub fn new_upval_open(&mut self, thread_id: usize, level: StackIdx) -> GcRef<UpVal> {
3017 self.mark_gc_check_needed();
3018 GcRef::new(UpVal::open(thread_id, level))
3019 }
3020 pub fn intern_or_create_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
3027 self.intern_str(bytes)
3028 }
3029 pub fn new_userdata(
3030 &mut self,
3031 _size: usize,
3032 _nuvalue: usize,
3033 ) -> Result<GcRef<LuaUserData>, LuaError> {
3034 Err(LuaError::runtime(format_args!(
3035 "new_userdata not implemented in this Phase-B build; use new_userdata_typed instead"
3036 )))
3037 }
3038 pub fn new_c_closure(&mut self, _f: LuaCFunction, _n: i32) -> Result<LuaClosure, LuaError> {
3039 Err(LuaError::runtime(format_args!("new_c_closure not implemented in this Phase-B build; use push_cclosure in lua_vm::api instead")))
3040 }
3041 pub fn push_closure(
3042 &mut self,
3043 proto_idx: usize,
3044 ci: CallInfoIdx,
3045 base: StackIdx,
3046 ra: StackIdx,
3047 ) -> Result<(), LuaError> {
3048 let parent_cl = self
3049 .ci_lua_closure(ci)
3050 .expect("push_closure: current frame is not a Lua closure");
3051 let child_proto = parent_cl.proto.p[proto_idx].clone();
3052 let nup = child_proto.upvalues.len();
3053 let mut upvals: Vec<std::cell::Cell<GcRef<UpVal>>> = Vec::with_capacity(nup);
3054 for i in 0..nup {
3055 let desc = &child_proto.upvalues[i];
3056 let uv = if desc.instack {
3057 let level = base + desc.idx as i32;
3058 crate::func::find_upval(self, level)
3059 } else {
3060 parent_cl.upval(desc.idx as usize)
3061 };
3062 upvals.push(std::cell::Cell::new(uv));
3063 }
3064 let cache_enabled = matches!(
3068 self.global().lua_version,
3069 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
3070 );
3071 if cache_enabled {
3072 if let Some(cached) = child_proto.cache.borrow().as_ref() {
3073 if cached.upvals.len() == nup
3074 && (0..nup).all(|i| GcRef::ptr_eq(&cached.upvals[i].get(), &upvals[i].get()))
3075 {
3076 let reused = cached.clone();
3077 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(reused)));
3078 return Ok(());
3079 }
3080 }
3081 }
3082 self.mark_gc_check_needed();
3085 let new_cl = GcRef::new(LuaClosureLua {
3086 proto: child_proto.clone(),
3087 upvals,
3088 });
3089 new_cl.account_buffer(new_cl.buffer_bytes() as isize);
3090 if cache_enabled {
3091 *child_proto.cache.borrow_mut() = Some(new_cl.clone());
3092 }
3093 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(new_cl)));
3094 Ok(())
3095 }
3096 pub fn new_tbc_upval(&mut self, idx: StackIdx) -> Result<(), LuaError> {
3097 crate::func::new_tbc_upval(self, idx)
3098 }
3099
3100 #[inline(always)]
3121 pub fn upvalue_get(&self, cl: &GcRef<LuaClosureLua>, n: usize) -> LuaValue {
3122 let uv = cl.upval(n);
3123 let (thread_id, idx) = match uv.try_open_payload() {
3124 Some(p) => p,
3125 None => return uv.closed_value(),
3126 };
3127 let current = self.cached_thread_id;
3128 let tid = thread_id as u64;
3129 if tid == current {
3130 return self.stack[idx.0 as usize].val;
3131 }
3132 self.upvalue_get_cross_thread(tid, idx)
3133 }
3134
3135 #[cold]
3136 #[inline(never)]
3137 fn upvalue_get_cross_thread(&self, tid: u64, idx: StackIdx) -> LuaValue {
3138 let entry_rc = {
3139 let g = self.global();
3140 g.threads.get(&tid).map(|e| e.state.clone())
3141 };
3142 if let Some(rc) = entry_rc {
3143 if let Ok(home_state) = rc.try_borrow() {
3144 return home_state.get_at(idx);
3145 }
3146 }
3147 let g = self.global();
3148 match g.cross_thread_upvals.get(&(tid, idx)) {
3149 Some(v) => *v,
3150 None => LuaValue::Nil,
3151 }
3152 }
3153 #[inline(always)]
3162 pub fn upvalue_set(
3163 &mut self,
3164 cl: &GcRef<LuaClosureLua>,
3165 n: usize,
3166 val: LuaValue,
3167 ) -> Result<(), LuaError> {
3168 let uv = cl.upval(n);
3169 match uv.try_open_payload() {
3170 Some((thread_id, idx)) => {
3171 let tid = thread_id as u64;
3172 let current = self.cached_thread_id;
3173 if tid == current {
3174 self.stack[idx.0 as usize].val = val;
3175 } else {
3176 self.upvalue_set_cross_thread(tid, idx, val)?;
3177 }
3178 }
3179 None => {
3180 uv.set_closed_value(val);
3181 }
3182 }
3183 if val.is_collectable() {
3184 self.gc_barrier_upval(&uv, &val);
3185 }
3186 Ok(())
3187 }
3188
3189 #[cold]
3190 #[inline(never)]
3191 fn upvalue_set_cross_thread(
3192 &mut self,
3193 tid: u64,
3194 idx: StackIdx,
3195 val: LuaValue,
3196 ) -> Result<(), LuaError> {
3197 let entry_rc = {
3198 let g = self.global();
3199 g.threads.get(&tid).map(|e| e.state.clone())
3200 };
3201 if let Some(rc) = entry_rc {
3202 if let Ok(mut home_state) = rc.try_borrow_mut() {
3203 home_state.set_at(idx, val);
3204 return Ok(());
3205 }
3206 }
3207 let mut g = self.global_mut();
3208 g.cross_thread_upvals.insert((tid, idx), val);
3209 Ok(())
3210 }
3211
3212 pub fn protected_call_raw(
3213 &mut self,
3214 func: StackIdx,
3215 nresults: i32,
3216 errfunc: StackIdx,
3217 ) -> Result<(), LuaError> {
3218 let ef = errfunc.0 as isize;
3219 let status = crate::do_::pcall(self, |s| s.call_no_yield(func, nresults), func, ef);
3220 match status {
3221 LuaStatus::Ok => Ok(()),
3222 LuaStatus::ErrSyntax => {
3223 let err_val = self.get_at(func);
3224 self.set_top(func);
3225 Err(LuaError::Syntax(err_val))
3226 }
3227 LuaStatus::Yield => {
3228 self.set_top(func);
3229 Err(LuaError::Yield)
3230 }
3231 _ => {
3232 let err_val = self.get_at(func);
3233 self.set_top(func);
3234 Err(LuaError::Runtime(err_val))
3235 }
3236 }
3237 }
3238 pub fn protected_parser(
3239 &mut self,
3240 z: crate::zio::ZIO,
3241 name: &[u8],
3242 mode: Option<&[u8]>,
3243 ) -> LuaStatus {
3244 crate::do_::protected_parser(self, z, name, mode)
3245 }
3246 pub fn do_call(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3247 crate::do_::call(self, func, nresults)
3248 }
3249 pub fn do_call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3250 crate::do_::callnoyield(self, func, nresults)
3251 }
3252 pub fn call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3253 crate::do_::callnoyield(self, func, nresults)
3254 }
3255 pub fn call_at(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3256 crate::do_::call(self, func, nresults)
3257 }
3258 #[inline(always)]
3259 pub fn call_known_c_at(&mut self, func: StackIdx, nresults: i32) -> Result<bool, LuaError> {
3260 crate::do_::call_known_c(self, func, nresults)
3261 }
3262 #[inline(always)]
3263 pub fn precall(
3264 &mut self,
3265 func: StackIdx,
3266 nresults: i32,
3267 ) -> Result<Option<CallInfoIdx>, LuaError> {
3268 crate::do_::precall(self, func, nresults)
3269 }
3270 #[inline(always)]
3271 pub fn pretailcall(
3272 &mut self,
3273 ci: CallInfoIdx,
3274 func: StackIdx,
3275 narg1: i32,
3276 delta: i32,
3277 ) -> Result<i32, LuaError> {
3278 crate::do_::pretailcall(self, ci, func, narg1, delta)
3279 }
3280 #[inline(always)]
3281 pub fn poscall<N: TryInto<i32>>(&mut self, ci: CallInfoIdx, nres: N) -> Result<(), LuaError>
3282 where
3283 <N as TryInto<i32>>::Error: std::fmt::Debug,
3284 {
3285 let n = nres.try_into().expect("poscall: nres out of i32 range");
3286 crate::do_::poscall(self, ci, n)
3287 }
3288 pub fn adjust_results(&mut self, nresults: i32) {
3289 const LUA_MULTRET: i32 = -1;
3290 if nresults <= LUA_MULTRET {
3291 let ci_idx = self.ci.as_usize();
3292 if self.call_info[ci_idx].top.0 < self.top.0 {
3293 self.call_info[ci_idx].top = self.top;
3294 }
3295 }
3296 }
3297 pub fn adjust_varargs(
3298 &mut self,
3299 ci: CallInfoIdx,
3300 nfixparams: i32,
3301 cl: &GcRef<lua_types::closure::LuaLClosure>,
3302 ) -> Result<(), LuaError> {
3303 crate::tagmethods::adjust_varargs(self, nfixparams, ci, &cl.0.proto)
3304 }
3305 pub fn get_varargs(&mut self, ci: CallInfoIdx, ra: StackIdx, n: i32) -> Result<i32, LuaError> {
3306 crate::tagmethods::get_varargs(self, ci, ra, n)?;
3307 Ok(0)
3308 }
3309
3310 pub fn close_upvals(&mut self, level: StackIdx) -> Result<(), LuaError> {
3311 crate::func::close_upval(self, level);
3312 Ok(())
3313 }
3314 pub fn close_upvals_status(&mut self, level: StackIdx, _status: i32) -> Result<(), LuaError> {
3315 crate::func::close_upval(self, level);
3316 Ok(())
3317 }
3318 pub fn close_upvals_from_base(&mut self, ci: CallInfoIdx) -> Result<(), LuaError> {
3319 let base = self.ci_base(ci);
3320 crate::func::close_upval(self, base);
3321 Ok(())
3322 }
3323
3324 pub fn arith_op(
3325 &mut self,
3326 op: i32,
3327 p1: &LuaValue,
3328 p2: &LuaValue,
3329 ) -> Result<LuaValue, LuaError> {
3330 let arith_op = match op {
3331 0 => lua_types::arith::ArithOp::Add,
3332 1 => lua_types::arith::ArithOp::Sub,
3333 2 => lua_types::arith::ArithOp::Mul,
3334 3 => lua_types::arith::ArithOp::Mod,
3335 4 => lua_types::arith::ArithOp::Pow,
3336 5 => lua_types::arith::ArithOp::Div,
3337 6 => lua_types::arith::ArithOp::Idiv,
3338 7 => lua_types::arith::ArithOp::Band,
3339 8 => lua_types::arith::ArithOp::Bor,
3340 9 => lua_types::arith::ArithOp::Bxor,
3341 10 => lua_types::arith::ArithOp::Shl,
3342 11 => lua_types::arith::ArithOp::Shr,
3343 12 => lua_types::arith::ArithOp::Unm,
3344 13 => lua_types::arith::ArithOp::Bnot,
3345 _ => return Err(LuaError::runtime(format_args!("invalid arith op {}", op))),
3346 };
3347 let mut res = LuaValue::Nil;
3348 if crate::object::raw_arith(self, arith_op, p1, p2, &mut res)? {
3349 Ok(res)
3350 } else {
3351 Err(LuaError::arith_error(p1, p2, "perform arithmetic on"))
3352 }
3353 }
3354 pub fn concat(&mut self, n: i32) -> Result<(), LuaError> {
3355 crate::vm::concat(self, n)
3356 }
3357 pub fn less_than(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3358 crate::vm::less_than(self, l, r)
3359 }
3360 pub fn less_equal(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3361 crate::vm::less_equal(self, l, r)
3362 }
3363 pub fn equal_obj(&self, _ctx: Option<&LuaValue>, l: &LuaValue, r: &LuaValue) -> bool {
3364 crate::vm::equal_obj(None, l, r).unwrap_or(false)
3365 }
3366 pub fn equal_obj_with_tm(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3367 crate::vm::equal_obj(Some(self), l, r)
3368 }
3369 pub fn obj_len(&mut self, v: &LuaValue) -> Result<LuaValue, LuaError> {
3370 match v {
3371 LuaValue::Table(_) => {
3372 let consult_len_tm =
3375 !matches!(self.global().lua_version, lua_types::LuaVersion::V51);
3376 let tm = if consult_len_tm {
3377 let mt = self.table_metatable(v);
3378 self.fast_tm_table(mt.as_ref(), TagMethod::Len)
3379 } else {
3380 LuaValue::Nil
3381 };
3382 if matches!(tm, LuaValue::Nil) {
3383 let n = self.table_length(v)?;
3384 return Ok(LuaValue::Int(n));
3385 }
3386 self.push(LuaValue::Nil);
3387 let slot = StackIdx(self.top.0 - 1);
3388 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3389 Ok(self.pop())
3390 }
3391 LuaValue::Str(s) => Ok(LuaValue::Int(s.len() as i64)),
3392 other => {
3393 let tm = crate::tagmethods::get_tm_by_obj(
3394 self,
3395 other,
3396 crate::tagmethods::TagMethod::Len,
3397 );
3398 if matches!(tm, LuaValue::Nil) {
3399 let mut msg = b"attempt to get length of a ".to_vec();
3400 msg.extend_from_slice(&self.obj_type_name(other));
3401 msg.extend_from_slice(b" value");
3402 return Err(crate::debug::prefixed_runtime_pub(self, msg));
3403 }
3404 self.push(LuaValue::Nil);
3405 let slot = StackIdx(self.top.0 - 1);
3406 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3407 Ok(self.pop())
3408 }
3409 }
3410 }
3411 pub fn obj_to_string(&mut self, idx: i32) -> Result<GcRef<LuaString>, LuaError> {
3412 let slot: StackIdx = if idx > 0 {
3413 let ci_func = self.current_call_info().func;
3414 ci_func + idx
3415 } else {
3416 debug_assert!(idx != 0, "invalid index");
3417 StackIdx((self.top_idx().0 as i32 + idx) as u32)
3418 };
3419 let val = self.get_at(slot);
3420 match val {
3421 LuaValue::Str(s) => Ok(s),
3422 LuaValue::Int(_) | LuaValue::Float(_) => {
3423 let s = crate::object::num_to_string(self, &val)?;
3424 self.set_at(slot, LuaValue::Str(s.clone()));
3425 Ok(s)
3426 }
3427 _ => Err(LuaError::type_error(&val, "convert to string")),
3428 }
3429 }
3430 pub fn coerce_to_string(&mut self, idx: StackIdx) -> Result<GcRef<LuaString>, LuaError> {
3431 let val = self.get_at(idx);
3432 match val {
3433 LuaValue::Str(s) => Ok(s),
3434 LuaValue::Int(_) | LuaValue::Float(_) => {
3435 let s = crate::object::num_to_string(self, &val)?;
3436 self.set_at(idx, LuaValue::Str(s.clone()));
3437 Ok(s)
3438 }
3439 _ => Err(LuaError::type_error(&val, "convert to string")),
3440 }
3441 }
3442 pub fn str_to_num(&mut self, s: &[u8]) -> Option<(LuaValue, usize)> {
3443 let mut out = LuaValue::Nil;
3444 let sz = crate::object::str2num(s, &mut out);
3445 if sz == 0 {
3446 None
3447 } else {
3448 Some((out, sz))
3449 }
3450 }
3451
3452 #[inline(always)]
3453 pub fn fast_get(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3454 let LuaValue::Table(tbl) = t else {
3455 return Ok(None);
3456 };
3457 let v = tbl.get(k);
3458 if matches!(v, LuaValue::Nil) {
3459 Ok(None)
3460 } else {
3461 Ok(Some(v))
3462 }
3463 }
3464 #[inline(always)]
3465 pub fn fast_get_int(&mut self, t: &LuaValue, k: i64) -> Result<Option<LuaValue>, LuaError> {
3466 let LuaValue::Table(tbl) = t else {
3467 return Ok(None);
3468 };
3469 let v = tbl.get_int(k);
3470 if matches!(v, LuaValue::Nil) {
3471 Ok(None)
3472 } else {
3473 Ok(Some(v))
3474 }
3475 }
3476 #[inline(always)]
3477 pub fn fast_get_short_str(
3478 &mut self,
3479 t: &LuaValue,
3480 k: &LuaValue,
3481 ) -> Result<Option<LuaValue>, LuaError> {
3482 let LuaValue::Table(tbl) = t else {
3483 return Ok(None);
3484 };
3485 let LuaValue::Str(s) = k else {
3486 return Ok(None);
3487 };
3488 let v = tbl.get_short_str(s);
3489 if matches!(v, LuaValue::Nil) {
3490 Ok(None)
3491 } else {
3492 Ok(Some(v))
3493 }
3494 }
3495 #[inline(always)]
3496 pub fn fast_tm_table(&mut self, t: Option<&GcRef<LuaTable>>, tm: TagMethod) -> LuaValue {
3497 let Some(mt) = t else {
3498 return LuaValue::Nil;
3499 };
3500 debug_assert!((tm as u8) <= TagMethod::Eq as u8);
3501 let ename = self.global().tmname[tm as usize].clone();
3502 mt.get_short_str(&ename)
3503 }
3504 pub fn fast_tm_ud(&mut self, u: &GcRef<LuaUserData>, tm: TagMethod) -> LuaValue {
3505 let mt = u.metatable();
3507 self.fast_tm_table(mt.as_ref(), tm)
3508 }
3509
3510 pub fn table_get_with_tm(&mut self, t: &LuaValue, k: &LuaValue) -> Result<LuaValue, LuaError> {
3511 if let LuaValue::Table(tbl) = t {
3517 if !tbl.has_metatable() {
3518 return Ok(tbl.get(k));
3519 }
3520 }
3521 if let Some(v) = self.fast_get(t, k)? {
3522 return Ok(v);
3523 }
3524 let res = self.top_idx();
3525 self.push(LuaValue::Nil);
3526 crate::vm::finish_get(self, t.clone(), k.clone(), res, true, None, None)?;
3527 let value = self.get_at(res);
3528 self.pop();
3529 Ok(value)
3530 }
3531 #[inline]
3546 pub fn table_set_with_tm(
3547 &mut self,
3548 t: &LuaValue,
3549 k: LuaValue,
3550 v: LuaValue,
3551 ) -> Result<(), LuaError> {
3552 if let LuaValue::Table(tbl) = t {
3553 if !tbl.has_metatable() {
3554 self.gc_table_barrier_back(tbl, &v);
3555 return self.table_raw_set(t, k, v);
3556 }
3557 }
3558 if self.fast_get(t, &k)?.is_some() {
3559 self.gc_value_barrier_back(t, &v);
3560 return self.table_raw_set(t, k, v);
3561 }
3562 crate::vm::finish_set(self, t.clone(), k, v, true, None, None)
3563 }
3564 #[inline]
3565 pub fn table_raw_set(
3566 &mut self,
3567 t: &LuaValue,
3568 k: LuaValue,
3569 v: LuaValue,
3570 ) -> Result<(), LuaError> {
3571 let LuaValue::Table(tbl) = t else {
3572 return Err(LuaError::type_error(t, "index"));
3573 };
3574 let tbl = tbl.clone();
3575 tbl.raw_set(self, k, v)
3576 }
3577 #[inline]
3578 pub fn table_array_set(
3579 &mut self,
3580 t: &LuaValue,
3581 idx: usize,
3582 v: LuaValue,
3583 ) -> Result<(), LuaError> {
3584 let LuaValue::Table(tbl) = t else {
3585 return Err(LuaError::type_error(t, "index"));
3586 };
3587 let tbl = tbl.clone();
3588 tbl.raw_set_int(self, idx as i64 + 1, v)
3589 }
3590 pub fn table_ensure_array(&mut self, t: &LuaValue, n: usize) -> Result<(), LuaError> {
3591 let LuaValue::Table(tbl) = t else {
3592 return Err(LuaError::type_error(t, "index"));
3593 };
3594 if n > tbl.array_len() {
3595 tbl.resize(self, n, 0)?;
3596 }
3597 Ok(())
3598 }
3599 pub fn table_length(&mut self, t: &LuaValue) -> Result<i64, LuaError> {
3600 let LuaValue::Table(tbl) = t else {
3601 return Err(LuaError::type_error(t, "get length of"));
3602 };
3603 Ok(tbl.getn() as i64)
3604 }
3605 pub fn table_metatable(&mut self, v: &LuaValue) -> Option<GcRef<LuaTable>> {
3606 match v {
3607 LuaValue::Table(t) => t.metatable(),
3608 LuaValue::UserData(u) => u.metatable(),
3609 other => {
3610 let idx = other.base_type() as usize;
3611 self.global().mt[idx].clone()
3612 }
3613 }
3614 }
3615 pub fn table_resize(
3616 &mut self,
3617 t: &GcRef<LuaTable>,
3618 na: usize,
3619 nh: usize,
3620 ) -> Result<(), LuaError> {
3621 self.mark_gc_check_needed();
3622 t.resize(self, na, nh)
3623 }
3624 pub fn table_getn(&self, t: &GcRef<LuaTable>) -> i64 {
3625 let mut i: i64 = 1;
3633 loop {
3634 let v = t.get_int(i);
3635 if matches!(v, LuaValue::Nil) {
3636 return i - 1;
3637 }
3638 i += 1;
3639 }
3640 }
3641
3642 pub fn try_bin_tm(
3643 &mut self,
3644 p1: &LuaValue,
3645 p1_idx: Option<StackIdx>,
3646 p2: &LuaValue,
3647 p2_idx: Option<StackIdx>,
3648 res: StackIdx,
3649 tm: lua_types::tagmethod::TagMethod,
3650 ) -> Result<(), LuaError> {
3651 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3652 crate::tagmethods::try_bin_tm(self, p1, p1_idx, p2, p2_idx, res, event)
3653 }
3654 pub fn try_bin_i_tm(
3655 &mut self,
3656 p1: &LuaValue,
3657 p1_idx: Option<StackIdx>,
3658 imm: i64,
3659 flip: bool,
3660 res: StackIdx,
3661 tm: lua_types::tagmethod::TagMethod,
3662 ) -> Result<(), LuaError> {
3663 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3664 crate::tagmethods::try_bini_tm(self, p1, p1_idx, imm, flip, res, event)
3665 }
3666 pub fn try_bin_assoc_tm(
3667 &mut self,
3668 p1: &LuaValue,
3669 p1_idx: Option<StackIdx>,
3670 p2: &LuaValue,
3671 p2_idx: Option<StackIdx>,
3672 flip: bool,
3673 res: StackIdx,
3674 tm: lua_types::tagmethod::TagMethod,
3675 ) -> Result<(), LuaError> {
3676 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3677 crate::tagmethods::try_bin_assoc_tm(self, p1, p1_idx, p2, p2_idx, flip, res, event)
3678 }
3679 pub fn try_concat_tm(&mut self, _p1: &LuaValue, _p2: &LuaValue) -> Result<(), LuaError> {
3680 crate::tagmethods::try_concat_tm(self)
3681 }
3682 pub fn call_tm(
3683 &mut self,
3684 f: LuaValue,
3685 p1: &LuaValue,
3686 p2: &LuaValue,
3687 p3: &LuaValue,
3688 ) -> Result<(), LuaError> {
3689 crate::tagmethods::call_tm(self, f, p1.clone(), p2.clone(), p3.clone())
3690 }
3691 pub fn call_tm_res(
3692 &mut self,
3693 f: LuaValue,
3694 p1: &LuaValue,
3695 p2: &LuaValue,
3696 res: StackIdx,
3697 ) -> Result<(), LuaError> {
3698 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)
3699 }
3700 pub fn call_tm_res_bool(
3701 &mut self,
3702 f: LuaValue,
3703 p1: &LuaValue,
3704 p2: &LuaValue,
3705 ) -> Result<bool, LuaError> {
3706 let res = self.top_idx();
3707 self.push(LuaValue::Nil);
3708 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)?;
3709 let result = self.get_at(res).clone();
3710 self.pop();
3711 Ok(!matches!(result, LuaValue::Nil | LuaValue::Bool(false)))
3712 }
3713 pub fn call_order_tm(
3714 &mut self,
3715 p1: &LuaValue,
3716 p2: &LuaValue,
3717 tm: lua_types::tagmethod::TagMethod,
3718 ) -> Result<bool, LuaError> {
3719 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3720 crate::tagmethods::call_order_tm(self, p1, p2, event)
3721 }
3722 pub fn call_order_i_tm(
3723 &mut self,
3724 p1: &LuaValue,
3725 v2: i64,
3726 flip: bool,
3727 isfloat: bool,
3728 tm: lua_types::tagmethod::TagMethod,
3729 ) -> Result<bool, LuaError> {
3730 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3731 crate::tagmethods::call_orderi_tm(self, p1, v2 as i32, flip, isfloat, event)
3732 }
3733
3734 #[inline(always)]
3735 pub fn proto_code(
3736 &self,
3737 cl: &GcRef<lua_types::closure::LuaLClosure>,
3738 pc: u32,
3739 ) -> lua_types::opcode::Instruction {
3740 cl.proto.code[pc as usize]
3741 }
3742 #[inline(always)]
3743 pub fn proto_const(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> LuaValue {
3744 cl.proto.k[idx].clone()
3745 }
3746 #[inline(always)]
3752 pub fn proto_const_int(
3753 &self,
3754 cl: &GcRef<lua_types::closure::LuaLClosure>,
3755 idx: usize,
3756 ) -> Option<i64> {
3757 match &cl.proto.k[idx] {
3758 LuaValue::Int(v) => Some(*v),
3759 _ => None,
3760 }
3761 }
3762 #[inline(always)]
3766 pub fn proto_const_num(
3767 &self,
3768 cl: &GcRef<lua_types::closure::LuaLClosure>,
3769 idx: usize,
3770 ) -> Option<f64> {
3771 match &cl.proto.k[idx] {
3772 LuaValue::Float(f) => Some(*f),
3773 LuaValue::Int(v) => Some(*v as f64),
3774 _ => None,
3775 }
3776 }
3777 pub fn get_proto_instr(&self, ci: CallInfoIdx, pc: u32) -> lua_types::opcode::Instruction {
3778 let cl = self
3779 .ci_lua_closure(ci)
3780 .expect("get_proto_instr: CallInfo does not hold a Lua closure");
3781 cl.proto.code[pc as usize]
3782 }
3783 pub fn trace_call(&mut self, _idx: CallInfoIdx) -> Result<bool, LuaError> {
3788 Ok(crate::debug::trace_call(self)? != 0)
3789 }
3790 pub fn trace_exec(&mut self, _idx: CallInfoIdx, pc: u32) -> Result<bool, LuaError> {
3793 Ok(crate::debug::trace_exec(self, pc)? != 0)
3794 }
3795 pub fn hook_call(&mut self, idx: CallInfoIdx) -> Result<(), LuaError> {
3796 crate::do_::hookcall(self, idx)
3797 }
3798 #[inline(always)]
3799 fn gc_step_flags(&self) -> Option<(bool, bool)> {
3800 let g = self.global();
3801 if !g.is_gc_running() {
3802 return None;
3803 }
3804 let should_collect = g.heap.would_collect();
3805 let has_finalizers = g.finalizers.has_to_be_finalized();
3806 if should_collect || has_finalizers {
3807 Some((should_collect, has_finalizers))
3808 } else {
3809 None
3810 }
3811 }
3812
3813 #[inline(always)]
3814 fn should_check_gc(&mut self) -> bool {
3815 if self.gc_check_needed {
3816 return true;
3817 }
3818 if self.global().finalizers.has_to_be_finalized() {
3819 self.gc_check_needed = true;
3820 return true;
3821 }
3822 false
3823 }
3824
3825 #[inline(always)]
3826 pub(crate) fn mark_gc_check_needed(&mut self) {
3827 self.gc_check_needed = true;
3828 }
3829
3830 #[inline(always)]
3831 pub fn gc_check_step(&mut self) {
3832 if !self.allowhook {
3833 return;
3834 }
3835 if !self.should_check_gc() {
3836 return;
3837 }
3838 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3839 self.gc_check_needed = false;
3840 return;
3841 };
3842 if should_collect || has_finalizers {
3843 if should_collect {
3844 self.gc().check_step();
3845 }
3846 crate::api::run_pending_finalizers(self);
3847 self.gc_check_needed = true;
3848 }
3849 let should_keep_checking = {
3850 let g = self.global();
3851 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3852 };
3853 self.gc_check_needed = should_keep_checking;
3854 }
3855 #[inline(always)]
3856 pub fn gc_cond_step(&mut self) {
3857 if !self.allowhook {
3858 return;
3859 }
3860 if !self.should_check_gc() {
3861 return;
3862 }
3863 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3864 self.gc_check_needed = false;
3865 return;
3866 };
3867 if should_collect || has_finalizers {
3868 if should_collect {
3869 self.gc().check_step();
3870 }
3871 crate::api::run_pending_finalizers(self);
3872 self.gc_check_needed = true;
3873 }
3874 let should_keep_checking = {
3875 let g = self.global();
3876 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3877 };
3878 self.gc_check_needed = should_keep_checking;
3879 }
3880 pub fn gc_barrier_back(&mut self, t: &dyn std::any::Any, v: &LuaValue) {
3881 self.gc().barrier_back(t, v);
3882 }
3883 #[inline(always)]
3884 pub fn gc_value_barrier_back(&mut self, t: &LuaValue, v: &LuaValue) {
3885 if !v.is_collectable() {
3886 return;
3887 }
3888 if let LuaValue::Table(tbl) = t {
3889 self.gc_table_barrier_back(tbl, v);
3890 } else {
3891 self.gc_barrier_back(t, v);
3892 }
3893 }
3894 #[inline(always)]
3895 pub fn gc_table_barrier_back(&mut self, t: &GcRef<LuaTable>, v: &LuaValue) {
3896 if !v.is_collectable() {
3897 return;
3898 }
3899 self.gc().table_barrier_back(t, v);
3900 }
3901 pub fn gc_barrier_upval(&mut self, uv: &GcRef<UpVal>, v: &LuaValue) {
3902 self.gc().barrier(uv, v);
3903 }
3904 pub fn is_main_thread(&mut self) -> bool {
3910 let g = self.global();
3911 g.current_thread_id == g.main_thread_id
3912 }
3913 pub fn obj_type_name<'v>(&self, v: &'v LuaValue) -> std::borrow::Cow<'static, [u8]> {
3914 match v {
3915 LuaValue::LightUserData(_) => std::borrow::Cow::Borrowed(b"light userdata"),
3916 LuaValue::Table(t) => {
3917 if let Some(mt) = t.metatable() {
3918 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3919 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3920 }
3921 }
3922 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3923 }
3924 LuaValue::UserData(u) => {
3925 if let Some(mt) = u.metatable() {
3926 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3927 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3928 }
3929 }
3930 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3931 }
3932 _ => std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type())),
3933 }
3934 }
3935
3936 pub fn full_type_name(&mut self, v: &LuaValue) -> Result<Vec<u8>, LuaError> {
3937 crate::tagmethods::obj_type_name(self, v)
3938 }
3939 pub fn emit_warning(&mut self, _msg: &[u8], _to_cont: bool) {
3940 warning(self, _msg, _to_cont)
3941 }
3942}
3943
3944pub struct GcHandle<'a> {
3950 _state: &'a mut LuaState,
3951}
3952
3953struct CollectRoots<'a> {
3960 global: &'a GlobalState,
3961 thread: &'a LuaState,
3962}
3963
3964#[derive(Clone, Copy)]
3965enum HeapCollectMode {
3966 Full,
3967 Step,
3968 Minor,
3969}
3970
3971impl<'a> lua_gc::Trace for CollectRoots<'a> {
3972 fn trace(&self, m: &mut lua_gc::Marker) {
3973 self.global.trace(m);
3974 self.thread.trace(m);
3975 }
3976}
3977
3978#[derive(Clone, Copy)]
3979enum BarrierKind {
3980 Forward,
3981 Backward,
3982}
3983
3984fn barrier_lua_value<P>(
3985 heap: &lua_gc::Heap,
3986 parent: GcRef<P>,
3987 child: &LuaValue,
3988 generational: bool,
3989 kind: BarrierKind,
3990) where
3991 P: lua_gc::Trace + 'static,
3992{
3993 if !child.is_collectable() {
3994 return;
3995 }
3996 if generational && matches!(kind, BarrierKind::Backward) {
3997 heap.generational_backward_barrier(parent.0);
3998 }
3999 match child {
4000 LuaValue::Str(c) => barrier_gc_child(heap, parent, *c, generational, kind),
4001 LuaValue::Table(c) => barrier_gc_child(heap, parent, *c, generational, kind),
4002 LuaValue::Function(LuaClosure::Lua(c)) => {
4003 barrier_gc_child(heap, parent, *c, generational, kind)
4004 }
4005 LuaValue::Function(LuaClosure::C(c)) => {
4006 barrier_gc_child(heap, parent, *c, generational, kind)
4007 }
4008 LuaValue::UserData(c) => barrier_gc_child(heap, parent, *c, generational, kind),
4009 LuaValue::Thread(c) => barrier_gc_child(heap, parent, *c, generational, kind),
4010 LuaValue::Nil
4011 | LuaValue::Bool(_)
4012 | LuaValue::Int(_)
4013 | LuaValue::Float(_)
4014 | LuaValue::LightUserData(_)
4015 | LuaValue::Function(LuaClosure::LightC(_)) => {}
4016 }
4017}
4018
4019fn barrier_gc_child<P, C>(
4020 heap: &lua_gc::Heap,
4021 parent: GcRef<P>,
4022 child: GcRef<C>,
4023 generational: bool,
4024 kind: BarrierKind,
4025) where
4026 P: lua_gc::Trace + 'static,
4027 C: lua_gc::Trace + 'static,
4028{
4029 if generational && matches!(kind, BarrierKind::Forward) {
4030 heap.generational_forward_barrier(parent.0, child.0);
4031 } else if matches!(kind, BarrierKind::Backward) {
4032 heap.barrier_back(parent.0, child.0);
4033 } else {
4034 heap.barrier(parent.0, child.0);
4035 }
4036}
4037
4038fn barrier_child_any<P>(
4039 heap: &lua_gc::Heap,
4040 parent: GcRef<P>,
4041 child: &dyn std::any::Any,
4042 generational: bool,
4043 kind: BarrierKind,
4044) where
4045 P: lua_gc::Trace + 'static,
4046{
4047 if let Some(v) = child.downcast_ref::<LuaValue>() {
4048 barrier_lua_value(heap, parent, v, generational, kind);
4049 } else if let Some(c) = child.downcast_ref::<GcRef<LuaString>>() {
4050 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4051 } else if let Some(c) = child.downcast_ref::<GcRef<LuaTable>>() {
4052 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4053 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureLua>>() {
4054 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4055 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureC>>() {
4056 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4057 } else if let Some(c) = child.downcast_ref::<GcRef<LuaUserData>>() {
4058 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4059 } else if let Some(c) = child.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
4060 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4061 } else if let Some(c) = child.downcast_ref::<GcRef<LuaProto>>() {
4062 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4063 } else if let Some(c) = child.downcast_ref::<GcRef<UpVal>>() {
4064 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4065 }
4066}
4067
4068fn barrier_any(
4069 heap: &lua_gc::Heap,
4070 parent: &dyn std::any::Any,
4071 child: &dyn std::any::Any,
4072 generational: bool,
4073 kind: BarrierKind,
4074) {
4075 if let Some(v) = parent.downcast_ref::<LuaValue>() {
4076 match v {
4077 LuaValue::Str(p) => barrier_child_any(heap, *p, child, generational, kind),
4078 LuaValue::Table(p) => barrier_child_any(heap, *p, child, generational, kind),
4079 LuaValue::Function(LuaClosure::Lua(p)) => {
4080 barrier_child_any(heap, *p, child, generational, kind)
4081 }
4082 LuaValue::Function(LuaClosure::C(p)) => {
4083 barrier_child_any(heap, *p, child, generational, kind)
4084 }
4085 LuaValue::UserData(p) => barrier_child_any(heap, *p, child, generational, kind),
4086 LuaValue::Thread(p) => barrier_child_any(heap, *p, child, generational, kind),
4087 LuaValue::Nil
4088 | LuaValue::Bool(_)
4089 | LuaValue::Int(_)
4090 | LuaValue::Float(_)
4091 | LuaValue::LightUserData(_)
4092 | LuaValue::Function(LuaClosure::LightC(_)) => {}
4093 }
4094 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaString>>() {
4095 barrier_child_any(heap, p.clone(), child, generational, kind);
4096 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaTable>>() {
4097 barrier_child_any(heap, p.clone(), child, generational, kind);
4098 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureLua>>() {
4099 barrier_child_any(heap, p.clone(), child, generational, kind);
4100 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureC>>() {
4101 barrier_child_any(heap, p.clone(), child, generational, kind);
4102 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaUserData>>() {
4103 barrier_child_any(heap, p.clone(), child, generational, kind);
4104 } else if let Some(p) = parent.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
4105 barrier_child_any(heap, p.clone(), child, generational, kind);
4106 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaProto>>() {
4107 barrier_child_any(heap, p.clone(), child, generational, kind);
4108 } else if let Some(p) = parent.downcast_ref::<GcRef<UpVal>>() {
4109 barrier_child_any(heap, p.clone(), child, generational, kind);
4110 }
4111}
4112
4113fn trace_reachable_threads(
4114 global: &GlobalState,
4115 _current_thread_id: u64,
4116 marker: &mut lua_gc::Marker,
4117) {
4118 use lua_gc::Trace;
4119
4120 loop {
4121 let visited_before = marker.visited_count();
4122 for (id, entry) in global.threads.iter() {
4123 if thread_entry_marked_alive(marker, *id, entry) {
4124 if let Ok(thread) = entry.state.try_borrow() {
4125 thread.trace(marker);
4126 }
4127 }
4128 }
4129 marker.drain_gray_queue();
4130 if marker.visited_count() == visited_before {
4131 break;
4132 }
4133 }
4134}
4135
4136fn thread_entry_marked_alive(
4137 marker: &lua_gc::Marker,
4138 id: u64,
4139 entry: &ThreadRegistryEntry,
4140) -> bool {
4141 marker.is_marked_or_old(entry.value.0) && entry.value.id == id
4142}
4143
4144fn lua_value_marked_or_old(marker: &lua_gc::Marker, value: &LuaValue) -> bool {
4145 match value {
4146 LuaValue::Str(v) => marker.is_marked_or_old(v.0),
4147 LuaValue::Table(v) => marker.is_marked_or_old(v.0),
4148 LuaValue::Function(LuaClosure::Lua(v)) => marker.is_marked_or_old(v.0),
4149 LuaValue::Function(LuaClosure::C(v)) => marker.is_marked_or_old(v.0),
4150 LuaValue::UserData(v) => marker.is_marked_or_old(v.0),
4151 LuaValue::Thread(v) => marker.is_marked_or_old(v.0),
4152 LuaValue::Nil
4153 | LuaValue::Bool(_)
4154 | LuaValue::Int(_)
4155 | LuaValue::Float(_)
4156 | LuaValue::LightUserData(_)
4157 | LuaValue::Function(LuaClosure::LightC(_)) => true,
4158 }
4159}
4160
4161fn lua_value_identity(value: &LuaValue) -> Option<usize> {
4162 match value {
4163 LuaValue::Str(v) => Some(v.identity()),
4164 LuaValue::Table(v) => Some(v.identity()),
4165 LuaValue::Function(LuaClosure::Lua(v)) => Some(v.identity()),
4166 LuaValue::Function(LuaClosure::C(v)) => Some(v.identity()),
4167 LuaValue::UserData(v) => Some(v.identity()),
4168 LuaValue::Thread(v) => Some(v.identity()),
4169 LuaValue::Nil
4170 | LuaValue::Bool(_)
4171 | LuaValue::Int(_)
4172 | LuaValue::Float(_)
4173 | LuaValue::LightUserData(_)
4174 | LuaValue::Function(LuaClosure::LightC(_)) => None,
4175 }
4176}
4177
4178fn finalizer_marked_or_old(marker: &lua_gc::Marker, object: &FinalizerObject) -> bool {
4179 match object {
4180 FinalizerObject::Table(t) => marker.is_marked_or_old(t.0),
4181 FinalizerObject::UserData(u) => marker.is_marked_or_old(u.0),
4182 }
4183}
4184
4185fn weak_snapshot_tables<'a>(
4186 snapshot: &'a lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>>,
4187) -> impl Iterator<Item = &'a GcRef<LuaTable>> {
4188 snapshot
4189 .weak_values
4190 .iter()
4191 .chain(snapshot.ephemeron.iter())
4192 .chain(snapshot.all_weak.iter())
4193}
4194
4195fn close_open_upvalues_for_unreachable_threads(global: &GlobalState, marker: &mut lua_gc::Marker) {
4196 use lua_gc::Trace;
4197
4198 let mut closed_values = Vec::<LuaValue>::new();
4199 for (id, entry) in global.threads.iter() {
4200 if entry.value.id != *id {
4201 continue;
4202 }
4203 if thread_entry_marked_alive(marker, *id, entry) {
4204 continue;
4205 }
4206 let Ok(thread) = entry.state.try_borrow() else {
4207 continue;
4208 };
4209 for uv in thread.openupval.iter() {
4210 if !marker.is_visited(uv.identity()) {
4211 continue;
4212 }
4213 let Some((thread_id, idx)) = uv.try_open_payload() else {
4214 continue;
4215 };
4216 if thread_id as u64 != *id {
4217 continue;
4218 }
4219 let value = thread.get_at(idx);
4220 uv.close_with(value.clone());
4221 closed_values.push(value);
4222 }
4223 }
4224 for value in closed_values {
4225 value.trace(marker);
4226 }
4227 marker.drain_gray_queue();
4228}
4229
4230fn record_live_interned_strings(
4231 global: &GlobalState,
4232 marker: &lua_gc::Marker,
4233 live_ids: &std::cell::RefCell<Vec<usize>>,
4234) {
4235 let mut live = live_ids.borrow_mut();
4236 for s in global.interned_lt.values() {
4237 let id = s.identity();
4238 if marker.is_visited(id) {
4239 live.push(id);
4240 }
4241 }
4242}
4243
4244fn retain_live_interned_strings(global: &mut GlobalState, mut live_ids: Vec<usize>) {
4245 if live_ids.is_empty() {
4246 global.interned_lt.clear();
4247 return;
4248 }
4249 if live_ids.len() == global.interned_lt.len() {
4250 return;
4251 }
4252 live_ids.sort_unstable();
4253 live_ids.dedup();
4254 global
4255 .interned_lt
4256 .retain(|_, s| live_ids.binary_search(&s.identity()).is_ok());
4257}
4258
4259impl<'a> GcHandle<'a> {
4260 pub fn check_step(&self) {
4267 if !self._state.global().is_gc_running() {
4268 return;
4269 }
4270 if self._state.global().is_gen_mode() {
4271 let should_collect = {
4272 let g = self._state.global();
4273 g.heap.would_collect() || g.gc_debt() > 0
4274 };
4275 if should_collect {
4276 self.generational_step();
4277 }
4278 } else {
4279 self.collect_via_heap(false);
4280 }
4281 }
4282
4283 pub fn full_collect(&self) {
4285 if self._state.global().is_gen_mode() {
4286 self.fullgen();
4287 } else {
4288 self.collect_via_heap(true);
4289 }
4290 }
4291
4292 fn negative_debt(bytes: usize) -> isize {
4293 -(bytes.min(isize::MAX as usize) as isize)
4294 }
4295
4296 fn set_minor_debt(&self) {
4297 let mut g = self._state.global_mut();
4298 let total = g.total_bytes();
4299 let growth = (total / 100).saturating_mul(g.genminormul as usize);
4300 g.heap
4301 .set_threshold_bytes(total.saturating_add(growth.max(1)));
4302 set_debt(&mut *g, Self::negative_debt(growth));
4303 }
4304
4305 fn set_pause_debt(&self) {
4306 let mut g = self._state.global_mut();
4307 let total = g.total_bytes();
4308 let pause = g.gc_pause_param().max(0) as usize;
4309 let threshold = g.gc_estimate.max(1).saturating_mul(pause) / 100;
4310 let debt = if threshold > total {
4311 Self::negative_debt(threshold - total)
4312 } else {
4313 0
4314 };
4315 let heap_threshold = if threshold > total {
4316 threshold
4317 } else {
4318 total.saturating_add(1)
4319 };
4320 g.heap.set_threshold_bytes(heap_threshold);
4321 set_debt(&mut *g, debt);
4322 }
4323
4324 fn enter_incremental_mode(&self) {
4325 let mut g = self._state.global_mut();
4326 g.heap.reset_all_ages();
4327 g.finalizers.reset_generation_boundaries();
4328 g.gckind = GcKind::Incremental as u8;
4329 g.lastatomic = 0;
4330 }
4331
4332 fn enter_generational_mode(&self) -> usize {
4333 self.collect_via_heap_mode(HeapCollectMode::Full);
4334 let numobjs = {
4335 let mut g = self._state.global_mut();
4336 g.heap.promote_all_to_old();
4337 g.finalizers.promote_all_pending_to_old();
4338 g.heap.allgc_count()
4339 };
4340 let total = self._state.global().total_bytes();
4341 {
4342 let mut g = self._state.global_mut();
4343 g.gckind = GcKind::Generational as u8;
4344 g.lastatomic = 0;
4345 g.gc_estimate = total;
4346 }
4347 self.set_minor_debt();
4348 numobjs
4349 }
4350
4351 fn fullgen(&self) -> usize {
4352 self.enter_incremental_mode();
4353 self.enter_generational_mode()
4354 }
4355
4356 fn stepgenfull(&self, lastatomic: usize) {
4357 if self._state.global().gckind == GcKind::Generational as u8 {
4358 self.enter_incremental_mode();
4359 }
4360 self.collect_via_heap_mode(HeapCollectMode::Full);
4361 let newatomic = self._state.global().heap.allgc_count().max(1);
4362 if newatomic < lastatomic.saturating_add(lastatomic >> 3) {
4363 {
4364 let mut g = self._state.global_mut();
4365 g.heap.promote_all_to_old();
4366 g.finalizers.promote_all_pending_to_old();
4367 }
4368 let total = self._state.global().total_bytes();
4369 {
4370 let mut g = self._state.global_mut();
4371 g.gckind = GcKind::Generational as u8;
4372 g.lastatomic = 0;
4373 g.gc_estimate = total;
4374 }
4375 self.set_minor_debt();
4376 } else {
4377 {
4378 let mut g = self._state.global_mut();
4379 g.heap.reset_all_ages();
4380 g.finalizers.reset_generation_boundaries();
4381 }
4382 let total = self._state.global().total_bytes();
4383 {
4384 let mut g = self._state.global_mut();
4385 g.gckind = GcKind::Incremental as u8;
4386 g.lastatomic = newatomic;
4387 g.gc_estimate = total;
4388 }
4389 self.set_pause_debt();
4390 }
4391 }
4392
4393 fn collect_via_heap(&self, force: bool) {
4402 self.collect_via_heap_mode(if force {
4403 HeapCollectMode::Full
4404 } else {
4405 HeapCollectMode::Step
4406 });
4407 }
4408
4409 fn collect_via_heap_mode(&self, mode: HeapCollectMode) {
4410 use lua_gc::Trace;
4411 let state_ref: &LuaState = &*self._state;
4412
4413 if matches!(mode, HeapCollectMode::Step) {
4419 let g = state_ref.global.borrow();
4420 if !g.heap.would_collect() {
4421 return;
4422 }
4423 }
4424
4425 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4429 let mut g = state_ref.global.borrow_mut();
4430 g.weak_tables_registry.live_snapshot_by_kind()
4431 };
4432
4433 let weak_table_capacity = weak_tables_snapshot.len();
4438 let (pending_snapshot, thread_capacity, interned_capacity): (
4439 Vec<FinalizerObject>,
4440 usize,
4441 usize,
4442 ) = {
4443 let g = state_ref.global.borrow();
4444 let pending = match mode {
4445 HeapCollectMode::Minor => g.finalizers.pending_minor_snapshot(),
4446 HeapCollectMode::Full | HeapCollectMode::Step => g.finalizers.pending_snapshot(),
4447 };
4448 (pending, g.threads.len(), g.interned_lt.len())
4449 };
4450 let finalizer_capacity = pending_snapshot.len();
4451
4452 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4453 std::cell::RefCell::new(std::collections::HashSet::new());
4454 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4455 std::cell::RefCell::new(Vec::new());
4456 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4457 std::cell::RefCell::new(std::collections::HashSet::new());
4458 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4459 std::cell::RefCell::new(std::collections::HashSet::new());
4460 let live_interned_ids: std::cell::RefCell<Vec<usize>> = std::cell::RefCell::new(Vec::new());
4461 let collect_ran = std::cell::Cell::new(false);
4462
4463 {
4464 let global = state_ref.global.borrow();
4465 global.heap.unpause();
4466 let roots = CollectRoots {
4467 global: &*global,
4468 thread: state_ref,
4469 };
4470 let hook = |marker: &mut lua_gc::Marker| {
4471 collect_ran.set(true);
4472 alive_ids.borrow_mut().reserve(weak_table_capacity);
4473 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4474 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4475 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4476 live_interned_ids.borrow_mut().reserve(interned_capacity);
4477 trace_reachable_threads(&*global, global.current_thread_id, marker);
4478 close_open_upvalues_for_unreachable_threads(&*global, marker);
4479 loop {
4480 let visited_before = marker.visited_count();
4481 for t in &weak_tables_snapshot.ephemeron {
4482 if !marker.is_marked_or_old(t.0) {
4483 continue;
4484 }
4485 let to_mark = t.ephemeron_values_to_mark_with_value(&|v| {
4486 lua_value_marked_or_old(marker, v)
4487 });
4488 for v in &to_mark {
4489 v.trace(marker);
4490 }
4491 }
4492 marker.drain_gray_queue();
4493 if marker.visited_count() == visited_before {
4494 break;
4495 }
4496 }
4497 for pf in &pending_snapshot {
4498 if !finalizer_marked_or_old(marker, pf) {
4499 pf.mark(marker);
4500 finalizing_ids.borrow_mut().insert(pf.identity());
4501 newly_unreachable.borrow_mut().push(pf.clone());
4502 }
4503 }
4504 marker.drain_gray_queue();
4505 loop {
4506 let visited_before = marker.visited_count();
4507 for t in &weak_tables_snapshot.ephemeron {
4508 if !marker.is_marked_or_old(t.0) {
4509 continue;
4510 }
4511 let to_mark = t.ephemeron_values_to_mark_with_value(&|v| {
4512 lua_value_marked_or_old(marker, v)
4513 });
4514 for v in &to_mark {
4515 v.trace(marker);
4516 }
4517 }
4518 marker.drain_gray_queue();
4519 if marker.visited_count() == visited_before {
4520 break;
4521 }
4522 }
4523 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4524 let id = t.identity();
4525 if marker.is_marked_or_old(t.0) {
4526 let to_mark = {
4527 let finalizing = finalizing_ids.borrow();
4528 t.prune_weak_dead_with_value(
4529 &|v| lua_value_marked_or_old(marker, v),
4530 &|v| {
4531 lua_value_marked_or_old(marker, v)
4532 && lua_value_identity(v)
4533 .map_or(true, |id| !finalizing.contains(&id))
4534 },
4535 )
4536 };
4537 for v in &to_mark {
4538 v.trace(marker);
4539 }
4540 alive_ids.borrow_mut().insert(id);
4541 }
4542 }
4543 marker.drain_gray_queue();
4544 {
4545 let mut alive = alive_thread_ids.borrow_mut();
4546 for (id, entry) in global.threads.iter() {
4547 if thread_entry_marked_alive(marker, *id, entry) {
4548 alive.insert(*id);
4549 }
4550 }
4551 }
4552 record_live_interned_strings(&*global, marker, &live_interned_ids);
4553 };
4554 match mode {
4555 HeapCollectMode::Full => global.heap.full_collect_with_post_mark(&roots, hook),
4556 HeapCollectMode::Step => global.heap.step_with_post_mark(&roots, hook),
4557 HeapCollectMode::Minor => global.heap.minor_collect_with_post_mark(&roots, hook),
4558 }
4559 }
4560
4561 if !collect_ran.get() {
4562 return;
4563 }
4564
4565 let alive_set = alive_ids.into_inner();
4569 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4570 let alive_thread_ids = alive_thread_ids.into_inner();
4571 let live_interned_ids = live_interned_ids.into_inner();
4572 let mut g = state_ref.global.borrow_mut();
4573 retain_live_interned_strings(&mut *g, live_interned_ids);
4574 g.weak_tables_registry.retain_identities(&alive_set);
4575 let main_thread_id = g.main_thread_id;
4576 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4577 g.cross_thread_upvals
4578 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4579 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4583 for object in &promoted {
4584 if let Some(ptr) = object.heap_ptr() {
4585 g.heap.move_finobj_to_tobefnz(ptr);
4586 }
4587 }
4588 if matches!(mode, HeapCollectMode::Minor) {
4589 g.finalizers.finish_minor_collection();
4590 }
4591 }
4592
4593 pub fn generational_step(&self) -> bool {
4595 self.generational_step_with_major(true)
4596 }
4597
4598 pub fn generational_step_minor_only(&self) -> bool {
4604 self.generational_step_with_major(false)
4605 }
4606
4607 fn generational_step_with_major(&self, allow_major: bool) -> bool {
4608 let (lastatomic, majorbase, majorinc, should_major) = {
4609 let g = self._state.global();
4610 let majorbase = if g.gc_estimate == 0 {
4611 g.total_bytes()
4612 } else {
4613 g.gc_estimate
4614 };
4615 let majormul = g.gc_genmajormul_param().max(0) as usize;
4616 let majorinc = (majorbase / 100).saturating_mul(majormul);
4617 let debt_due = g.gc_debt() > 0 || g.heap.would_collect();
4618 let should_major =
4619 allow_major && debt_due && g.total_bytes() > majorbase.saturating_add(majorinc);
4620 (g.lastatomic, majorbase, majorinc, should_major)
4621 };
4622
4623 if lastatomic != 0 {
4624 self.stepgenfull(lastatomic);
4625 debug_assert!(self._state.global().is_gen_mode());
4626 return true;
4627 }
4628
4629 if should_major {
4630 let numobjs = self.fullgen();
4631 let after = self._state.global().total_bytes();
4632 if after < majorbase.saturating_add(majorinc / 2) {
4633 self.set_minor_debt();
4634 } else {
4635 {
4636 let mut g = self._state.global_mut();
4637 g.lastatomic = numobjs.max(1);
4638 }
4639 self.set_pause_debt();
4640 }
4641 } else {
4642 self.collect_via_heap_mode(HeapCollectMode::Minor);
4643 self.set_minor_debt();
4644 self._state.global_mut().gc_estimate = majorbase;
4645 }
4646
4647 debug_assert!(self._state.global().is_gen_mode());
4648 true
4649 }
4650
4651 pub fn step(&self) { }
4654
4655 pub fn incremental_step(&self, work_units: isize) -> bool {
4668 self.incremental_step_to_state(work_units, None)
4669 }
4670
4671 pub fn run_until_gc_state_for_test(&self, target: lua_gc::GcState) -> bool {
4676 self.incremental_step_to_state(isize::MAX / 4, Some(target));
4677 self._state.global().heap.gc_state() == target
4678 }
4679
4680 fn incremental_step_to_state(
4681 &self,
4682 work_units: isize,
4683 target: Option<lua_gc::GcState>,
4684 ) -> bool {
4685 use lua_gc::{StepBudget, StepOutcome, Trace};
4686 let state_ref: &LuaState = &*self._state;
4687
4688 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4689 let mut g = state_ref.global.borrow_mut();
4690 g.weak_tables_registry.live_snapshot_by_kind()
4691 };
4692
4693 let weak_table_capacity = weak_tables_snapshot.len();
4694 let (pending_snapshot, thread_capacity, interned_capacity): (
4695 Vec<FinalizerObject>,
4696 usize,
4697 usize,
4698 ) = {
4699 let g = state_ref.global.borrow();
4700 (
4701 g.finalizers.pending_snapshot(),
4702 g.threads.len(),
4703 g.interned_lt.len(),
4704 )
4705 };
4706 let finalizer_capacity = pending_snapshot.len();
4707
4708 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4709 std::cell::RefCell::new(std::collections::HashSet::new());
4710 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4711 std::cell::RefCell::new(Vec::new());
4712 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4713 std::cell::RefCell::new(std::collections::HashSet::new());
4714 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4715 std::cell::RefCell::new(std::collections::HashSet::new());
4716 let live_interned_ids: std::cell::RefCell<Vec<usize>> = std::cell::RefCell::new(Vec::new());
4717 let atomic_ran = std::cell::Cell::new(false);
4718
4719 let stop_target = {
4720 let g = state_ref.global.borrow();
4721 match (target, g.heap.gc_state()) {
4722 (Some(target), _) => Some(target),
4723 (None, lua_gc::GcState::CallFin) => None,
4724 (None, _) => Some(lua_gc::GcState::CallFin),
4725 }
4726 };
4727
4728 let outcome = {
4729 let global = state_ref.global.borrow();
4730 global.heap.unpause();
4731 let roots = CollectRoots {
4732 global: &*global,
4733 thread: state_ref,
4734 };
4735 let hook = |marker: &mut lua_gc::Marker| {
4736 atomic_ran.set(true);
4737 alive_ids.borrow_mut().reserve(weak_table_capacity);
4738 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4739 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4740 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4741 live_interned_ids.borrow_mut().reserve(interned_capacity);
4742 trace_reachable_threads(&*global, global.current_thread_id, marker);
4743 close_open_upvalues_for_unreachable_threads(&*global, marker);
4744 loop {
4745 let visited_before = marker.visited_count();
4746 for t in &weak_tables_snapshot.ephemeron {
4747 let t_id = t.identity();
4748 if !marker.is_visited(t_id) {
4749 continue;
4750 }
4751 let to_mark = t.ephemeron_values_to_mark(&|id| marker.is_visited(id));
4752 for v in &to_mark {
4753 v.trace(marker);
4754 }
4755 }
4756 marker.drain_gray_queue();
4757 if marker.visited_count() == visited_before {
4758 break;
4759 }
4760 }
4761 for pf in &pending_snapshot {
4762 if !marker.is_visited(pf.identity()) {
4763 pf.mark(marker);
4764 finalizing_ids.borrow_mut().insert(pf.identity());
4765 newly_unreachable.borrow_mut().push(pf.clone());
4766 }
4767 }
4768 marker.drain_gray_queue();
4769 loop {
4770 let visited_before = marker.visited_count();
4771 for t in &weak_tables_snapshot.ephemeron {
4772 let t_id = t.identity();
4773 if !marker.is_visited(t_id) {
4774 continue;
4775 }
4776 let to_mark = t.ephemeron_values_to_mark(&|id| marker.is_visited(id));
4777 for v in &to_mark {
4778 v.trace(marker);
4779 }
4780 }
4781 marker.drain_gray_queue();
4782 if marker.visited_count() == visited_before {
4783 break;
4784 }
4785 }
4786 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4787 let id = t.identity();
4788 if marker.is_visited(id) {
4789 let to_mark = {
4790 let finalizing = finalizing_ids.borrow();
4791 t.prune_weak_dead_with(&|id| marker.is_visited(id), &|id| {
4792 marker.is_visited(id) && !finalizing.contains(&id)
4793 })
4794 };
4795 for v in &to_mark {
4796 v.trace(marker);
4797 }
4798 alive_ids.borrow_mut().insert(id);
4799 }
4800 }
4801 marker.drain_gray_queue();
4802 {
4803 let mut alive = alive_thread_ids.borrow_mut();
4804 for (id, entry) in global.threads.iter() {
4805 if thread_entry_marked_alive(marker, *id, entry) {
4806 alive.insert(*id);
4807 }
4808 }
4809 }
4810 record_live_interned_strings(&*global, marker, &live_interned_ids);
4811 };
4812 let budget = StepBudget::from_work(work_units);
4813 if let Some(target) = stop_target {
4814 global
4815 .heap
4816 .incremental_run_until_state_with_post_mark(&roots, target, work_units, hook)
4817 } else {
4818 global
4819 .heap
4820 .incremental_step_with_post_mark(&roots, budget, hook)
4821 }
4822 };
4823
4824 if atomic_ran.get() {
4825 let alive_set = alive_ids.into_inner();
4826 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4827 let alive_thread_ids = alive_thread_ids.into_inner();
4828 let live_interned_ids = live_interned_ids.into_inner();
4829 let mut g = state_ref.global.borrow_mut();
4830 retain_live_interned_strings(&mut *g, live_interned_ids);
4831 g.weak_tables_registry.retain_identities(&alive_set);
4832 let main_thread_id = g.main_thread_id;
4833 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4834 g.cross_thread_upvals
4835 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4836 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4837 for object in &promoted {
4838 if let Some(ptr) = object.heap_ptr() {
4839 g.heap.move_finobj_to_tobefnz(ptr);
4840 }
4841 }
4842 }
4843
4844 let mut paused = matches!(outcome, StepOutcome::Paused);
4845 if target.is_none()
4846 && self._state.global().heap.gc_state() == lua_gc::GcState::CallFin
4847 && !self._state.global().finalizers.has_to_be_finalized()
4848 {
4849 paused = self._state.global().heap.finish_callfin_phase();
4850 }
4851
4852 paused
4853 }
4854
4855 pub fn prune_weak_tables_mark_only(&self) {
4862 use lua_gc::Trace;
4863 let state_ref: &LuaState = &*self._state;
4864
4865 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4866 let mut g = state_ref.global.borrow_mut();
4867 g.weak_tables_registry.live_snapshot_by_kind()
4868 };
4869 let interned_capacity = {
4870 let g = state_ref.global.borrow();
4871 g.interned_lt.len()
4872 };
4873
4874 let live_interned_ids: std::cell::RefCell<Vec<usize>> = std::cell::RefCell::new(Vec::new());
4875
4876 {
4877 let global = state_ref.global.borrow();
4878 global.heap.unpause();
4879 let roots = CollectRoots {
4880 global: &*global,
4881 thread: state_ref,
4882 };
4883 let hook = |marker: &mut lua_gc::Marker| {
4884 live_interned_ids.borrow_mut().reserve(interned_capacity);
4885 trace_reachable_threads(&*global, global.current_thread_id, marker);
4886 loop {
4887 let visited_before = marker.visited_count();
4888 for t in &weak_tables_snapshot.ephemeron {
4889 let t_id = t.identity();
4890 if !marker.is_visited(t_id) {
4891 continue;
4892 }
4893 let to_mark = t.ephemeron_values_to_mark(&|id| marker.is_visited(id));
4894 for v in &to_mark {
4895 v.trace(marker);
4896 }
4897 }
4898 marker.drain_gray_queue();
4899 if marker.visited_count() == visited_before {
4900 break;
4901 }
4902 }
4903 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4904 if marker.is_visited(t.identity()) {
4905 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4906 for v in &to_mark {
4907 v.trace(marker);
4908 }
4909 }
4910 }
4911 marker.drain_gray_queue();
4912 record_live_interned_strings(&*global, marker, &live_interned_ids);
4913 };
4914 global.heap.mark_only_with_post_mark(&roots, hook);
4915 }
4916
4917 let live_interned_ids = live_interned_ids.into_inner();
4918 let mut g = state_ref.global.borrow_mut();
4919 retain_live_interned_strings(&mut *g, live_interned_ids);
4920 }
4921
4922 pub fn change_mode(&self, mode: GcKind) {
4924 let old = self._state.global().gckind;
4925 if old == mode as u8 {
4926 self._state.global_mut().lastatomic = 0;
4927 return;
4928 }
4929 match mode {
4930 GcKind::Generational => {
4931 self.enter_generational_mode();
4932 }
4933 GcKind::Incremental => {
4934 self.enter_incremental_mode();
4935 }
4936 }
4937 }
4938
4939 pub fn fix_object<T: lua_gc::Trace + 'static>(&self, _o: &GcRef<T>) { }
4942
4943 pub fn free_all_objects(&self) {
4947 }
4949
4950 pub fn barrier(&self, p: &dyn std::any::Any, v: &LuaValue) {
4954 let g = self._state.global();
4955 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Forward);
4956 }
4957
4958 pub fn barrier_back(&self, p: &dyn std::any::Any, v: &LuaValue) {
4962 let g = self._state.global();
4963 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Backward);
4964 }
4965
4966 pub fn table_barrier_back(&self, p: &GcRef<LuaTable>, v: &LuaValue) {
4968 let g = self._state.global();
4969 barrier_lua_value(&g.heap, *p, v, g.is_gen_mode(), BarrierKind::Backward);
4970 }
4971
4972 pub fn obj_barrier(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4976 let g = self._state.global();
4977 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Forward);
4978 }
4979
4980 pub fn obj_barrier_back(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4983 let g = self._state.global();
4984 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Backward);
4985 }
4986}
4987
4988fn make_seed() -> u32 {
4997 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4998 {
4999 return crate::string::hash_bytes(b"lua-rs-wasm-seed", 0x9e37_79b9);
5000 }
5001
5002 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
5003 {
5004 use std::time::{SystemTime, UNIX_EPOCH};
5005 let t = SystemTime::now()
5006 .duration_since(UNIX_EPOCH)
5007 .map(|d| d.as_secs() as u32)
5008 .unwrap_or(0);
5009
5010 crate::string::hash_bytes(&t.to_le_bytes(), t)
5018 }
5019}
5020
5021pub(crate) fn set_debt(g: &mut GlobalState, mut debt: isize) {
5035 let tb = g.total_bytes() as isize;
5036 debug_assert!(tb > 0);
5037 if debt < tb.saturating_sub(isize::MAX) {
5039 debt = tb - isize::MAX;
5040 }
5041 g.gc_debt = debt;
5042}
5043
5044pub fn set_c_stack_limit(_state: &mut LuaState, _limit: u32) -> i32 {
5054 let _ = (_state, _limit);
5055 LUAI_MAXCCALLS as i32
5056}
5057
5058pub(crate) fn extend_ci(state: &mut LuaState) -> CallInfoIdx {
5075 debug_assert!(
5076 state.call_info[state.ci.0 as usize].next.is_none(),
5077 "extend_ci: current ci already has a cached next frame"
5078 );
5079
5080 let current_idx = state.ci;
5081 let new_idx = CallInfoIdx(state.call_info.len() as u32);
5083
5084 state.call_info.push(CallInfo {
5085 previous: Some(current_idx),
5086 next: None,
5087 u: CallInfoFrame::lua_default(),
5088 ..CallInfo::default()
5089 });
5090
5091 state.call_info[current_idx.0 as usize].next = Some(new_idx);
5092
5093 state.nci += 1;
5094
5095 new_idx
5096}
5097
5098fn free_ci(state: &mut LuaState) {
5119 let ci_idx = state.ci.0 as usize;
5120
5121 let mut next_opt = state.call_info[ci_idx].next.take();
5122
5123 while let Some(idx) = next_opt {
5124 next_opt = state.call_info[idx.0 as usize].next;
5125 state.nci = state.nci.saturating_sub(1);
5126 }
5127
5128 state.call_info.truncate(ci_idx + 1);
5131}
5132
5133pub(crate) fn shrink_ci(state: &mut LuaState) {
5160 let ci_idx = state.ci.0 as usize;
5161
5162 if state.call_info[ci_idx].next.is_none() {
5163 return;
5164 }
5165
5166 let free_count = state.call_info.len().saturating_sub(ci_idx + 1);
5167 if free_count <= 1 {
5168 return;
5169 }
5170
5171 let keep = free_count / 2;
5175 let removed = free_count - keep;
5176 let new_len = ci_idx + 1 + keep;
5177 state.call_info.truncate(new_len);
5178 state.nci = state.nci.saturating_sub(removed as u32);
5179
5180 if let Some(last) = state.call_info.last_mut() {
5182 last.next = None;
5183 }
5184}
5185
5186pub(crate) fn check_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
5198 if state.c_calls() == LUAI_MAXCCALLS {
5201 return Err(LuaError::runtime(format_args!("C stack overflow")));
5202 }
5203 if state.c_calls() >= (LUAI_MAXCCALLS / 10 * 11) {
5205 return Err(LuaError::with_status(LuaStatus::ErrErr));
5206 }
5207 Ok(())
5208}
5209
5210pub fn inc_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
5221 state.n_ccalls += 1;
5222 if state.c_calls() >= LUAI_MAXCCALLS {
5224 check_c_stack(state)?;
5225 }
5226 Ok(())
5227}
5228
5229fn stack_init(thread: &mut LuaState) {
5235 let total_slots = BASIC_STACK_SIZE + EXTRA_STACK;
5237 thread.stack = vec![StackValue::default(); total_slots];
5238
5239 thread.tbclist = Vec::new();
5243
5244 thread.top = StackIdx(0);
5249
5250 thread.stack_last = StackIdx(BASIC_STACK_SIZE as u32);
5251
5252 let base_ci = CallInfo {
5253 func: StackIdx(0),
5254 top: StackIdx(1 + LUA_MINSTACK as u32),
5255 previous: None,
5256 next: None,
5257 callstatus: CIST_C,
5258 call_metamethods: 0,
5259 nresults: 0,
5260 u: CallInfoFrame::c_default(),
5261 u2: CallInfoExtra::default(),
5262 };
5263
5264 if thread.call_info.is_empty() {
5265 thread.call_info.push(base_ci);
5266 } else {
5267 thread.call_info[0] = base_ci;
5268 thread.call_info.truncate(1);
5269 }
5270
5271 thread.stack[0] = StackValue {
5272 val: LuaValue::Nil,
5273 tbc_delta: 0,
5274 };
5275
5276 thread.top = StackIdx(1);
5277
5278 thread.ci = CallInfoIdx(0);
5279}
5280
5281fn free_stack(state: &mut LuaState) {
5282 if state.stack.is_empty() {
5283 return;
5284 }
5285 state.ci = CallInfoIdx(0);
5286 free_ci(state);
5287 debug_assert_eq!(state.nci, 0, "nci should be 0 after free_ci");
5288 state.stack.clear();
5290 state.stack.shrink_to_fit();
5291}
5292
5293fn init_registry(state: &mut LuaState) -> Result<(), LuaError> {
5294 let registry = state.new_table();
5296
5297 state.global_mut().l_registry = LuaValue::Table(registry.clone());
5299
5300 let globals = state.new_table();
5320 state.global_mut().globals = LuaValue::Table(globals);
5321 let loaded = state.new_table();
5322 state.global_mut().loaded = LuaValue::Table(loaded);
5323
5324 Ok(())
5325}
5326
5327fn lua_open(state: &mut LuaState) -> Result<(), LuaError> {
5328 stack_init(state);
5329 init_registry(state)?;
5330 crate::string::init(state)?;
5331 crate::tagmethods::init(state)?;
5332 state.global_mut().gcstp = 0;
5334 state.global().heap.unpause();
5335 state.global_mut().nilvalue = LuaValue::Nil;
5338 Ok(())
5340}
5341
5342fn preinit_thread(thread: &mut LuaState, global: Rc<RefCell<GlobalState>>) {
5343 thread.global = global;
5344 thread.stack = Vec::new();
5345 thread.call_info = Vec::new();
5346 thread.ci = CallInfoIdx(0);
5349 thread.nci = 0;
5350 thread.n_ccalls = 0;
5354 thread.hook = None;
5355 thread.hookmask = 0;
5356 thread.basehookcount = 0;
5357 thread.allowhook = true;
5358 thread.hookcount = thread.basehookcount;
5360
5361 {
5366 let (active, interval) = {
5367 let g = thread.global.borrow();
5368 (g.sandbox_active(), g.sandbox.interval.get())
5369 };
5370 if active {
5371 thread.hookmask = SANDBOX_COUNT_MASK;
5372 thread.basehookcount = interval;
5373 thread.hookcount = interval;
5374 }
5375 }
5376 thread.openupval = Vec::new();
5377 thread.status = LuaStatus::Ok as u8;
5378 thread.errfunc = 0;
5379 thread.oldpc = 0;
5380 thread.gc_check_needed = true;
5381}
5382
5383fn close_state(state: &mut LuaState) {
5384 let is_complete = state.global().is_complete();
5385
5386 if !is_complete {
5387 state.gc().free_all_objects();
5389 } else {
5390 state.ci = CallInfoIdx(0);
5391 state.gc().free_all_objects();
5394 }
5396
5397 state.global_mut().strt = StringPool::default();
5399
5400 free_stack(state);
5401
5402 }
5407
5408pub fn new_thread(state: &mut LuaState, initial_body: Option<LuaValue>) -> Result<(), LuaError> {
5437 state.gc().check_step();
5438
5439 let global_rc = state.global_rc();
5444 let hookmask = state.hookmask;
5445 let basehookcount = state.basehookcount;
5446
5447 let reserved_id = {
5448 let mut g = state.global_mut();
5449 let id = g.next_thread_id;
5450 g.next_thread_id += 1;
5451 id
5452 };
5453
5454 let mut new_thread = LuaState {
5455 status: LuaStatus::Ok as u8,
5456 allowhook: true,
5457 nci: 0,
5458 top: StackIdx(0),
5459 stack_last: StackIdx(0),
5460 stack: Vec::new(),
5461 ci: CallInfoIdx(0),
5462 call_info: Vec::new(),
5463 openupval: Vec::new(),
5464 tbclist: Vec::new(),
5465 global: global_rc.clone(),
5466 hook: None,
5467 hookmask: 0,
5468 basehookcount: 0,
5469 hookcount: 0,
5470 errfunc: 0,
5471 n_ccalls: 0,
5472 oldpc: 0,
5473 marked: 0,
5474 cached_thread_id: reserved_id,
5475 gc_check_needed: false,
5476 };
5477
5478 preinit_thread(&mut new_thread, global_rc);
5479
5480 new_thread.hookmask = hookmask;
5481 new_thread.basehookcount = basehookcount;
5482 new_thread.reset_hook_count();
5485
5486 stack_init(&mut new_thread);
5492
5493 if let Some(body) = initial_body {
5494 new_thread.push(body);
5495 }
5496
5497 let thread_ref: Rc<RefCell<LuaState>> = Rc::new(RefCell::new(new_thread));
5498
5499 let value = {
5500 let mut g = state.global_mut();
5501 let id = reserved_id;
5502 let value = GcRef::new(lua_types::value::LuaThread::new(id));
5503 g.threads.insert(
5504 id,
5505 ThreadRegistryEntry {
5506 state: thread_ref,
5507 value: value.clone(),
5508 },
5509 );
5510 value
5511 };
5512
5513 state.push(LuaValue::Thread(value));
5514
5515 Ok(())
5516}
5517
5518pub fn reset_thread(state: &mut LuaState, status: i32) -> i32 {
5540 state.ci = CallInfoIdx(0);
5541 let ci_idx = 0usize;
5542
5543 if !state.stack.is_empty() {
5545 state.stack[0].val = LuaValue::Nil;
5546 }
5547
5548 state.call_info[ci_idx].func = StackIdx(0);
5549 state.call_info[ci_idx].call_metamethods = 0;
5550 state.call_info[ci_idx].callstatus = CIST_C;
5551
5552 let mut status = if status == LuaStatus::Yield as i32 {
5553 LuaStatus::Ok as i32
5554 } else {
5555 status
5556 };
5557
5558 state.status = LuaStatus::Ok as u8;
5559
5560 let close_status = crate::do_::close_protected(state, StackIdx(1), LuaStatus::from_raw(status));
5561 status = close_status as i32;
5562
5563 if status != LuaStatus::Ok as i32 {
5564 crate::do_::set_error_obj(state, LuaStatus::from_raw(status), StackIdx(1));
5565 } else {
5566 state.top = StackIdx(1);
5567 }
5568
5569 let new_ci_top = StackIdx(state.top.0 + LUA_MINSTACK as u32);
5570 state.call_info[ci_idx].top = new_ci_top;
5571
5572 let needed = new_ci_top.0 as usize;
5575 if state.stack.len() < needed {
5576 state.stack.resize(needed, StackValue::default());
5577 }
5578
5579 status
5580}
5581
5582pub fn close_thread(state: &mut LuaState, from: Option<&LuaState>) -> i32 {
5596 state.n_ccalls = match from {
5598 Some(f) => f.c_calls(),
5599 None => 0,
5600 };
5601 let current_status = state.status as i32;
5602 let result = reset_thread(state, current_status);
5603 result
5604}
5605
5606pub fn reset_thread_api(state: &mut LuaState) -> i32 {
5615 close_thread(state, None)
5616}
5617
5618pub fn new_state() -> Option<LuaState> {
5654 let placeholder_str = GcRef::new(LuaString::placeholder());
5663
5664 let initial_white = 1u8 << WHITE0BIT;
5666
5667 let global = GlobalState {
5671 parser_hook: None,
5672 cli_argv: None,
5673 cli_preload: None,
5674 lua_version: lua_types::LuaVersion::default(),
5675 file_loader_hook: None,
5676 file_open_hook: None,
5677 stdout_hook: None,
5678 stderr_hook: None,
5679 stdin_hook: None,
5680 env_hook: None,
5681 unix_time_hook: None,
5682 cpu_clock_hook: None,
5683 local_offset_hook: None,
5684 entropy_hook: None,
5685 temp_name_hook: None,
5686 popen_hook: None,
5687 file_remove_hook: None,
5688 file_rename_hook: None,
5689 os_execute_hook: None,
5690 dynlib_load_hook: None,
5691 dynlib_symbol_hook: None,
5692 dynlib_unload_hook: None,
5693 sandbox: SandboxLimits::default(),
5694 gc_debt: 0,
5695 gc_estimate: 0,
5696 lastatomic: 0,
5697 strt: StringPool::default(),
5698 l_registry: LuaValue::Nil,
5699 external_roots: ExternalRootSet::default(),
5700 globals: LuaValue::Nil,
5701 loaded: LuaValue::Nil,
5702 nilvalue: LuaValue::Int(0),
5703 seed: make_seed(),
5704 currentwhite: initial_white,
5705 gcstate: GCS_PAUSE,
5706 gckind: GcKind::Incremental as u8,
5708 gcstopem: false,
5709 genminormul: LUAI_GENMINORMUL,
5710 genmajormul: (LUAI_GENMAJORMUL / 4) as u8,
5712 gcstp: GCSTPGC,
5713 gcemergency: false,
5714 gcpause: (LUAI_GCPAUSE / 4) as u8,
5715 gcstepmul: (LUAI_GCMUL / 4) as u8,
5716 gcstepsize: LUAI_GCSTEPSIZE,
5717 gc55_params: [20, 50, 68, 250, 200, 9600],
5720 sweepgc_cursor: 0,
5721 weak_tables_registry: lua_gc::WeakRegistry::default(),
5722 finalizers: lua_gc::FinalizerRegistry::default(),
5723 gc_finalizer_error: None,
5724 twups: Vec::new(),
5725 panic: None,
5726 mainthread: None,
5727 threads: std::collections::HashMap::new(),
5728 main_thread_value: GcRef::new(lua_types::value::LuaThread::new(0)),
5729 current_thread_id: 0,
5730 closing_thread_id: None,
5731 main_thread_id: 0,
5732 next_thread_id: 1,
5733 memerrmsg: placeholder_str.clone(),
5734 tmname: Vec::new(),
5735 mt: std::array::from_fn(|_| None),
5736 strcache: std::array::from_fn(|_| std::array::from_fn(|_| placeholder_str.clone())),
5737 interned_lt: InternedStringMap::default(),
5738 warnf: None,
5739 warn_mode: WarnMode::Off,
5740 test_warn_enabled: false,
5741 test_warn_on: false,
5742 test_warn_mode: TestWarnMode::Normal,
5743 test_warn_last_to_cont: false,
5744 test_warn_buffer: Vec::new(),
5745 c_functions: Vec::new(),
5746 heap: lua_gc::Heap::new(),
5747 cross_thread_upvals: std::collections::HashMap::new(),
5748 suspended_parent_stacks: Vec::new(),
5749 suspended_parent_open_upvals: Vec::new(),
5750 };
5751
5752 let global_rc = Rc::new(RefCell::new(global));
5753
5754 let initial_marked = initial_white;
5756
5757 let mut main_thread = LuaState {
5758 status: LuaStatus::Ok as u8,
5759 allowhook: true,
5760 nci: 0,
5761 top: StackIdx(0),
5762 stack_last: StackIdx(0),
5763 stack: Vec::new(),
5764 ci: CallInfoIdx(0),
5765 call_info: Vec::new(),
5766 openupval: Vec::new(),
5767 tbclist: Vec::new(),
5768 global: global_rc.clone(),
5769 hook: None,
5770 hookmask: 0,
5771 basehookcount: 0,
5772 hookcount: 0,
5773 errfunc: 0,
5774 n_ccalls: 0,
5775 oldpc: 0,
5776 marked: initial_marked,
5777 cached_thread_id: 0,
5778 gc_check_needed: false,
5779 };
5780
5781 preinit_thread(&mut main_thread, global_rc.clone());
5782
5783 main_thread.inc_nny();
5785
5786 match lua_open(&mut main_thread) {
5796 Ok(()) => {}
5797 Err(_) => {
5798 close_state(&mut main_thread);
5799 return None;
5800 }
5801 }
5802
5803 Some(main_thread)
5804}
5805
5806pub fn close(mut state: LuaState) {
5822 close_state(&mut state);
5827}
5828
5829pub(crate) fn warning(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5839 let test_warn_enabled = state.global().test_warn_enabled;
5840 if test_warn_enabled {
5841 test_warn(state, msg, to_cont);
5842 return;
5843 }
5844
5845 let has_warnf = state.global().warnf.is_some();
5854 if has_warnf {
5855 let mut warnf = state.global_mut().warnf.take();
5857 if let Some(ref mut f) = warnf {
5858 f(msg, to_cont);
5859 }
5860 state.global_mut().warnf = warnf;
5862 return;
5863 }
5864 default_warn(state, msg, to_cont);
5865}
5866
5867fn test_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5868 let is_control = {
5869 let g = state.global();
5870 !g.test_warn_last_to_cont && !to_cont && msg.first() == Some(&b'@')
5871 };
5872 if is_control {
5873 let mut g = state.global_mut();
5874 match &msg[1..] {
5875 b"off" => g.test_warn_on = false,
5876 b"on" => g.test_warn_on = true,
5877 b"normal" => g.test_warn_mode = TestWarnMode::Normal,
5878 b"allow" => g.test_warn_mode = TestWarnMode::Allow,
5879 b"store" => g.test_warn_mode = TestWarnMode::Store,
5880 _ => {}
5881 }
5882 return;
5883 }
5884
5885 let finished = {
5886 let mut g = state.global_mut();
5887 g.test_warn_last_to_cont = to_cont;
5888 g.test_warn_buffer.extend_from_slice(msg);
5889 if to_cont {
5890 None
5891 } else {
5892 Some((
5893 std::mem::take(&mut g.test_warn_buffer),
5894 g.test_warn_mode,
5895 g.test_warn_on,
5896 ))
5897 }
5898 };
5899
5900 let Some((message, mode, warn_on)) = finished else {
5901 return;
5902 };
5903 match mode {
5904 TestWarnMode::Normal => {
5905 if warn_on && message.first() == Some(&b'#') {
5906 write_warning_message(&message);
5907 }
5908 }
5909 TestWarnMode::Allow => {
5910 if warn_on {
5911 write_warning_message(&message);
5912 }
5913 }
5914 TestWarnMode::Store => {
5915 if let Ok(s) = state.intern_str(&message) {
5916 state.push(LuaValue::Str(s));
5917 let _ = crate::api::set_global(state, b"_WARN");
5918 }
5919 }
5920 }
5921}
5922
5923fn write_warning_message(message: &[u8]) {
5924 use std::io::Write;
5925 let stderr = std::io::stderr();
5926 let mut h = stderr.lock();
5927 let _ = h.write_all(b"Lua warning: ");
5928 let _ = h.write_all(message);
5929 let _ = h.write_all(b"\n");
5930}
5931
5932fn default_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5937 use std::io::Write;
5938 if !to_cont && msg.first() == Some(&b'@') {
5940 match &msg[1..] {
5941 b"off" => state.global_mut().warn_mode = WarnMode::Off,
5942 b"on" => state.global_mut().warn_mode = WarnMode::On,
5943 _ => {}
5944 }
5945 return;
5946 }
5947 let mode = state.global().warn_mode;
5948 match mode {
5949 WarnMode::Off => {}
5950 WarnMode::On | WarnMode::Cont => {
5951 let stderr = std::io::stderr();
5952 let mut h = stderr.lock();
5953 if mode == WarnMode::On {
5954 let _ = h.write_all(b"Lua warning: ");
5955 }
5956 let _ = h.write_all(msg);
5957 if to_cont {
5958 state.global_mut().warn_mode = WarnMode::Cont;
5959 } else {
5960 let _ = h.write_all(b"\n");
5961 state.global_mut().warn_mode = WarnMode::On;
5962 }
5963 }
5964 }
5965}
5966
5967#[cfg(test)]
5968mod tests {
5969 use super::*;
5970
5971 fn test_noop_cclosure(_: &mut LuaState) -> Result<usize, LuaError> {
5972 Ok(0)
5973 }
5974
5975 #[test]
5976 fn external_root_keys_reject_stale_slot_after_reuse() {
5977 let mut roots = ExternalRootSet::default();
5978
5979 let first = roots.insert(LuaValue::Int(1));
5980 assert_eq!(roots.len(), 1);
5981 assert_eq!(roots.get(first), Some(&LuaValue::Int(1)));
5982
5983 assert_eq!(roots.remove(first), Some(LuaValue::Int(1)));
5984 assert!(roots.get(first).is_none());
5985 assert!(roots.remove(first).is_none());
5986 assert_eq!(roots.len(), 0);
5987 assert_eq!(roots.vacant_len(), 1);
5988 assert!(roots.replace(first, LuaValue::Int(9)).is_none());
5989 assert!(roots.is_empty());
5990
5991 let second = roots.insert(LuaValue::Int(2));
5992 assert_eq!(first.index, second.index);
5993 assert_ne!(first, second);
5994 assert!(roots.get(first).is_none());
5995 assert_eq!(roots.get(second), Some(&LuaValue::Int(2)));
5996 assert!(roots.replace(first, LuaValue::Int(3)).is_none());
5997 }
5998
5999 #[test]
6000 fn external_roots_keep_heap_value_alive_until_unrooted() {
6001 let mut state = new_state().expect("state should initialize");
6002 let _heap_guard = {
6003 let g = state.global();
6004 lua_gc::HeapGuard::push(&g.heap)
6005 };
6006
6007 let table = state.new_table();
6008 assert_eq!(state.global().heap.allgc_count(), 1);
6009
6010 let key = state.external_root_value(LuaValue::Table(table));
6011 state.gc().full_collect();
6012 assert_eq!(state.global().heap.allgc_count(), 1);
6013 assert_eq!(state.global().external_roots.len(), 1);
6014
6015 assert!(state.external_unroot_value(key).is_some());
6016 state.gc().full_collect();
6017 assert_eq!(state.global().heap.allgc_count(), 0);
6018 assert!(state.global().external_roots.is_empty());
6019 }
6020
6021 #[test]
6022 fn table_buffer_accounting_refunds_on_sweep() {
6023 let mut state = new_state().expect("state should initialize");
6024 let _heap_guard = {
6025 let g = state.global();
6026 lua_gc::HeapGuard::push(&g.heap)
6027 };
6028
6029 let table = state.new_table();
6030 let key = state.external_root_value(LuaValue::Table(table));
6031 let header_bytes = state.global().heap.bytes_used();
6032 assert!(header_bytes > 0);
6033
6034 for i in 1..=128 {
6035 table
6036 .raw_set_int(&mut state, i, LuaValue::Int(i))
6037 .expect("integer table insert should succeed");
6038 }
6039 let grown_bytes = state.global().heap.bytes_used();
6040 assert!(
6041 grown_bytes > header_bytes,
6042 "table array/hash buffer growth must be charged to the GC heap"
6043 );
6044
6045 state.gc().full_collect();
6046 assert_eq!(
6047 state.global().heap.bytes_used(),
6048 grown_bytes,
6049 "rooted table buffer bytes should remain charged after collection"
6050 );
6051
6052 assert!(state.external_unroot_value(key).is_some());
6053 state.gc().full_collect();
6054 assert_eq!(state.global().heap.bytes_used(), 0);
6055 assert_eq!(state.global().heap.allgc_count(), 0);
6056 }
6057
6058 #[test]
6059 fn userdata_buffer_accounting_refunds_on_sweep() {
6060 let mut state = new_state().expect("state should initialize");
6061 let _heap_guard = {
6062 let g = state.global();
6063 lua_gc::HeapGuard::push(&g.heap)
6064 };
6065
6066 let payload_len = 4096;
6067 let userdata = state
6068 .new_userdata_typed(b"accounting", payload_len, 3)
6069 .expect("userdata allocation should succeed");
6070 state.pop_n(1);
6071 let key = state.external_root_value(LuaValue::UserData(userdata));
6072 let allocated_bytes = state.global().heap.bytes_used();
6073 assert!(
6074 allocated_bytes > payload_len,
6075 "userdata payload bytes must be charged to the GC heap"
6076 );
6077
6078 state.gc().full_collect();
6079 assert_eq!(
6080 state.global().heap.bytes_used(),
6081 allocated_bytes,
6082 "rooted userdata payload bytes should remain charged after collection"
6083 );
6084
6085 assert!(state.external_unroot_value(key).is_some());
6086 state.gc().full_collect();
6087 assert_eq!(state.global().heap.bytes_used(), 0);
6088 assert_eq!(state.global().heap.allgc_count(), 0);
6089 }
6090
6091 #[test]
6092 fn cclosure_upvalue_accounting_refunds_on_sweep() {
6093 let mut state = new_state().expect("state should initialize");
6094 let _heap_guard = {
6095 let g = state.global();
6096 lua_gc::HeapGuard::push(&g.heap)
6097 };
6098
6099 let nupvalues = 64;
6100 for i in 0..nupvalues {
6101 state.push(LuaValue::Int(i as i64));
6102 }
6103 crate::api::push_cclosure(&mut state, test_noop_cclosure, nupvalues as i32)
6104 .expect("C closure creation should succeed");
6105 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6106 panic!("expected heavy C closure");
6107 };
6108 let expected_payload = ccl.buffer_bytes();
6109 let key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
6110 state.pop_n(1);
6111 let allocated_bytes = state.global().heap.bytes_used();
6112 assert!(
6113 allocated_bytes >= expected_payload,
6114 "C closure upvalue vector bytes must be charged to the GC heap"
6115 );
6116
6117 state.gc().full_collect();
6118 assert_eq!(
6119 state.global().heap.bytes_used(),
6120 allocated_bytes,
6121 "rooted C closure payload bytes should remain charged after collection"
6122 );
6123
6124 assert!(state.external_unroot_value(key).is_some());
6125 state.gc().full_collect();
6126 assert_eq!(state.global().heap.bytes_used(), 0);
6127 assert_eq!(state.global().heap.allgc_count(), 0);
6128 }
6129
6130 #[test]
6131 fn proto_and_lclosure_accounting_refunds_on_sweep() {
6132 let mut state = new_state().expect("state should initialize");
6133 let _heap_guard = {
6134 let g = state.global();
6135 lua_gc::HeapGuard::push(&g.heap)
6136 };
6137
6138 let mut proto = LuaProto::placeholder();
6139 proto.code = vec![lua_types::opcode::Instruction(0); 2048];
6140 proto.lineinfo = vec![0; 2048];
6141 proto.k = vec![LuaValue::Int(1); 512];
6142 let expected_proto_payload = proto.buffer_bytes();
6143 let proto = GcRef::new(proto);
6144 proto.account_buffer(expected_proto_payload as isize);
6145
6146 let closure = state.new_lclosure(proto, 16);
6147 let expected_closure_payload = closure.buffer_bytes();
6148 let key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6149 let allocated_bytes = state.global().heap.bytes_used();
6150 assert!(
6151 allocated_bytes >= expected_proto_payload + expected_closure_payload,
6152 "proto and Lua closure vector bytes must be charged to the GC heap"
6153 );
6154
6155 state.gc().full_collect();
6156 assert_eq!(
6157 state.global().heap.bytes_used(),
6158 allocated_bytes,
6159 "rooted proto and Lua closure payload bytes should remain charged after collection"
6160 );
6161
6162 assert!(state.external_unroot_value(key).is_some());
6163 state.gc().full_collect();
6164 assert_eq!(state.global().heap.bytes_used(), 0);
6165 assert_eq!(state.global().heap.allgc_count(), 0);
6166 }
6167
6168 #[test]
6169 fn string_buffer_accounting_refunds_on_sweep() {
6170 let mut state = new_state().expect("state should initialize");
6171 let _heap_guard = {
6172 let g = state.global();
6173 lua_gc::HeapGuard::push(&g.heap)
6174 };
6175
6176 let payload = vec![b'x'; crate::string::MAX_SHORT_LEN + 4096];
6177 let string = state
6178 .intern_str(&payload)
6179 .expect("long string should allocate");
6180 let key = state.external_root_value(LuaValue::Str(string));
6181 let allocated_bytes = state.global().heap.bytes_used();
6182 assert!(
6183 allocated_bytes > payload.len(),
6184 "long string backing bytes must be charged to the GC heap"
6185 );
6186
6187 state.gc().full_collect();
6188 assert_eq!(
6189 state.global().heap.bytes_used(),
6190 allocated_bytes,
6191 "rooted string buffer bytes should remain charged after collection"
6192 );
6193
6194 assert!(state.external_unroot_value(key).is_some());
6195 state.gc().full_collect();
6196 assert_eq!(state.global().heap.bytes_used(), 0);
6197 assert_eq!(state.global().heap.allgc_count(), 0);
6198 }
6199
6200 #[test]
6201 fn interned_short_string_cache_does_not_root_unreferenced_string() {
6202 let mut state = new_state().expect("state should initialize");
6203 let _heap_guard = {
6204 let g = state.global();
6205 lua_gc::HeapGuard::push(&g.heap)
6206 };
6207
6208 let payload = b"weak-cache-probe-a";
6209 let string = state
6210 .intern_str(payload)
6211 .expect("short string should intern");
6212 let id = string.identity();
6213 assert!(state.global().interned_lt.contains_key(&payload[..]));
6214 assert!(state.global().heap.allocation_token(id).is_some());
6215
6216 state.gc().full_collect();
6217 assert!(!state.global().interned_lt.contains_key(&payload[..]));
6218 assert_eq!(state.global().heap.allocation_token(id), None);
6219 }
6220
6221 #[test]
6222 fn interned_short_string_cache_keeps_reachable_string_until_unrooted() {
6223 let mut state = new_state().expect("state should initialize");
6224 let _heap_guard = {
6225 let g = state.global();
6226 lua_gc::HeapGuard::push(&g.heap)
6227 };
6228
6229 let payload = b"weak-cache-probe-b";
6230 let string = state
6231 .intern_str(payload)
6232 .expect("short string should intern");
6233 let id = string.identity();
6234 let key = state.external_root_value(LuaValue::Str(string));
6235
6236 state.gc().full_collect();
6237 assert!(state.global().interned_lt.contains_key(&payload[..]));
6238 assert!(state.global().heap.allocation_token(id).is_some());
6239
6240 assert!(state.external_unroot_value(key).is_some());
6241 state.gc().full_collect();
6242 assert!(!state.global().interned_lt.contains_key(&payload[..]));
6243 assert_eq!(state.global().heap.allocation_token(id), None);
6244 }
6245
6246 #[test]
6247 fn gc_phase_predicates_follow_heap_state() {
6248 let mut state = new_state().expect("state should initialize");
6249 let _heap_guard = {
6250 let g = state.global();
6251 lua_gc::HeapGuard::push(&g.heap)
6252 };
6253
6254 {
6255 let mut g = state.global_mut();
6256 g.gckind = GcKind::Incremental as u8;
6257 g.lastatomic = 0;
6258 assert!(!g.is_gen_mode());
6259 g.lastatomic = 1;
6260 assert!(g.is_gen_mode());
6261 g.lastatomic = 0;
6262 }
6263
6264 let mut roots = Vec::new();
6265 for _ in 0..16 {
6266 let table = state.new_table();
6267 roots.push(state.external_root_value(LuaValue::Table(table)));
6268 }
6269
6270 let mut saw_keep = false;
6271 let mut saw_sweep = false;
6272 for _ in 0..128 {
6273 state.gc().incremental_step(1);
6274 let g = state.global();
6275 let heap_state = g.heap.gc_state();
6276 assert_eq!(g.keep_invariant(), heap_state.is_invariant());
6277 assert_eq!(g.is_sweep_phase(), heap_state.is_sweep());
6278 saw_keep |= g.keep_invariant();
6279 saw_sweep |= g.is_sweep_phase();
6280 if heap_state.is_pause() && saw_keep && saw_sweep {
6281 break;
6282 }
6283 }
6284
6285 assert!(
6286 saw_keep,
6287 "incremental cycle should expose an invariant phase"
6288 );
6289 assert!(saw_sweep, "incremental cycle should expose a sweep phase");
6290
6291 for key in roots {
6292 assert!(state.external_unroot_value(key).is_some());
6293 }
6294 state.gc().full_collect();
6295 }
6296
6297 #[test]
6298 fn gc_barrier_keeps_new_child_stored_in_black_parent() {
6299 let mut state = new_state().expect("state should initialize");
6300 let _heap_guard = {
6301 let g = state.global();
6302 lua_gc::HeapGuard::push(&g.heap)
6303 };
6304
6305 let parent = state.new_table();
6306 let parent_key = state.external_root_value(LuaValue::Table(parent));
6307 state.gc().incremental_step(1);
6308 assert!(
6309 state.global().keep_invariant(),
6310 "test setup should leave the parent marked during an active cycle"
6311 );
6312
6313 let child = state.new_table();
6314 let parent_value = LuaValue::Table(parent);
6315 let child_value = LuaValue::Table(child);
6316 parent
6317 .raw_set_int(&mut state, 1, child_value)
6318 .expect("table store should succeed");
6319 state.gc_barrier_back(&parent_value, &child_value);
6320
6321 for _ in 0..128 {
6322 if state.gc().incremental_step(1) {
6323 break;
6324 }
6325 }
6326
6327 assert_eq!(state.global().heap.allgc_count(), 2);
6328 assert_eq!(
6329 parent.get_int(1).as_table().map(|t| t.identity()),
6330 Some(child.identity())
6331 );
6332
6333 assert!(state.external_unroot_value(parent_key).is_some());
6334 state.gc().full_collect();
6335 assert_eq!(state.global().heap.allgc_count(), 0);
6336 }
6337
6338 #[test]
6339 fn generational_mode_promotes_and_barriers_age_objects() {
6340 let mut state = new_state().expect("state should initialize");
6341 let _heap_guard = {
6342 let g = state.global();
6343 lua_gc::HeapGuard::push(&g.heap)
6344 };
6345
6346 let parent = state.new_table();
6347 let parent_key = state.external_root_value(LuaValue::Table(parent));
6348
6349 state.gc().change_mode(GcKind::Generational);
6350 assert_eq!(parent.0.age(), lua_gc::GcAge::Old);
6351 assert_eq!(parent.0.color(), lua_gc::Color::Black);
6352 let majorbase = state.global().gc_estimate;
6353 assert!(majorbase > 0);
6354 assert!(state.global().gc_debt() <= 0);
6355
6356 let child = state.new_table();
6357 let parent_value = LuaValue::Table(parent);
6358 let child_value = LuaValue::Table(child);
6359 parent
6360 .raw_set_int(&mut state, 1, child_value.clone())
6361 .expect("table store should succeed");
6362 state.gc_barrier_back(&parent_value, &child_value);
6363 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched1);
6364 assert_eq!(parent.0.color(), lua_gc::Color::Gray);
6365 assert_eq!(child.0.age(), lua_gc::GcAge::New);
6366
6367 let metatable = state.new_table();
6368 parent.set_metatable(Some(metatable));
6369 state.gc().obj_barrier(&parent, &metatable);
6370 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old0);
6371
6372 assert!(state.gc().generational_step_minor_only());
6373 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched2);
6374 assert_eq!(child.0.age(), lua_gc::GcAge::Survival);
6375 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old1);
6376 assert_eq!(state.global().gc_estimate, majorbase);
6377 assert!(state.global().gc_debt() <= 0);
6378
6379 state.gc().change_mode(GcKind::Incremental);
6380 assert_eq!(parent.0.age(), lua_gc::GcAge::New);
6381 assert_eq!(child.0.age(), lua_gc::GcAge::New);
6382 assert_eq!(metatable.0.age(), lua_gc::GcAge::New);
6383
6384 assert!(state.external_unroot_value(parent_key).is_some());
6385 state.gc().full_collect();
6386 }
6387
6388 #[test]
6389 fn generational_upvalue_write_barrier_marks_young_child_old0() {
6390 let mut state = new_state().expect("state should initialize");
6391 let _heap_guard = {
6392 let g = state.global();
6393 lua_gc::HeapGuard::push(&g.heap)
6394 };
6395
6396 let proto = state.new_proto();
6397 let closure = state.new_lclosure(proto, 1);
6398 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6399 state.gc().change_mode(GcKind::Generational);
6400 let uv = closure.upval(0);
6401 assert_eq!(uv.0.age(), lua_gc::GcAge::Old);
6402
6403 let child = state.new_table();
6404 state
6405 .upvalue_set(&closure, 0, LuaValue::Table(child))
6406 .expect("closed upvalue write should succeed");
6407 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
6408
6409 assert!(state.external_unroot_value(closure_key).is_some());
6410 state.gc().full_collect();
6411 }
6412
6413 #[test]
6414 fn cclosure_setupvalue_replaces_upvalue() {
6415 let mut state = new_state().expect("state should initialize");
6416 let _heap_guard = {
6417 let g = state.global();
6418 lua_gc::HeapGuard::push(&g.heap)
6419 };
6420
6421 let first = state.new_table();
6422 state.push(LuaValue::Table(first));
6423 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6424 .expect("C closure creation should succeed");
6425 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6426 panic!("expected heavy C closure");
6427 };
6428
6429 let second = state.new_table();
6430 state.push(LuaValue::Table(second));
6431 let name =
6432 crate::api::setup_value(&mut state, -2, 1).expect("C closure upvalue should exist");
6433
6434 assert!(name.is_empty());
6435 let upvalues = ccl.upvalues.borrow();
6436 let LuaValue::Table(actual) = upvalues[0].clone() else {
6437 panic!("expected table upvalue");
6438 };
6439 assert_eq!(actual.identity(), second.identity());
6440 }
6441
6442 #[test]
6443 fn generational_cclosure_setupvalue_barrier_marks_young_child_old0() {
6444 let mut state = new_state().expect("state should initialize");
6445 let _heap_guard = {
6446 let g = state.global();
6447 lua_gc::HeapGuard::push(&g.heap)
6448 };
6449
6450 state.push(LuaValue::Nil);
6451 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6452 .expect("C closure creation should succeed");
6453 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6454 panic!("expected heavy C closure");
6455 };
6456 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
6457
6458 state.gc().change_mode(GcKind::Generational);
6459 assert_eq!(ccl.0.age(), lua_gc::GcAge::Old);
6460
6461 let child = state.new_table();
6462 state.push(LuaValue::Table(child));
6463 crate::api::setup_value(&mut state, -2, 1).expect("C closure upvalue should exist");
6464
6465 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
6466
6467 assert!(state.external_unroot_value(closure_key).is_some());
6468 state.gc().full_collect();
6469 }
6470
6471 #[test]
6472 fn generational_closure_upvalue_slot_barrier_marks_new_upval_old0() {
6473 let mut state = new_state().expect("state should initialize");
6474 let _heap_guard = {
6475 let g = state.global();
6476 lua_gc::HeapGuard::push(&g.heap)
6477 };
6478
6479 let proto = state.new_proto();
6480 let closure = state.new_lclosure(proto, 1);
6481 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6482 state.gc().change_mode(GcKind::Generational);
6483 assert_eq!(closure.0.age(), lua_gc::GcAge::Old);
6484
6485 let replacement = state.new_upval_closed(LuaValue::Nil);
6486 closure.set_upval(0, replacement);
6487 state.gc().obj_barrier(&closure, &replacement);
6488 assert_eq!(replacement.0.age(), lua_gc::GcAge::Old0);
6489
6490 assert!(state.external_unroot_value(closure_key).is_some());
6491 state.gc().full_collect();
6492 }
6493
6494 #[test]
6495 fn cross_thread_upvalue_mirror_traces_values_as_roots() {
6496 let mut state = new_state().expect("state should initialize");
6497 let _heap_guard = {
6498 let g = state.global();
6499 lua_gc::HeapGuard::push(&g.heap)
6500 };
6501
6502 let mirrored = state.new_table();
6503 state
6504 .global_mut()
6505 .cross_thread_upvals
6506 .insert((999, StackIdx(0)), LuaValue::Table(mirrored));
6507
6508 state.gc().full_collect();
6509 assert_eq!(state.global().heap.allgc_count(), 1);
6510
6511 state.global_mut().cross_thread_upvals.clear();
6512 state.gc().full_collect();
6513 assert_eq!(state.global().heap.allgc_count(), 0);
6514 }
6515
6516 #[test]
6517 fn generational_full_collect_promotes_new_survivors_to_old() {
6518 let mut state = new_state().expect("state should initialize");
6519 let _heap_guard = {
6520 let g = state.global();
6521 lua_gc::HeapGuard::push(&g.heap)
6522 };
6523
6524 state.gc().change_mode(GcKind::Generational);
6525 let table = state.new_table();
6526 let table_key = state.external_root_value(LuaValue::Table(table));
6527 assert_eq!(table.0.age(), lua_gc::GcAge::New);
6528
6529 state.gc().full_collect();
6530 assert_eq!(table.0.age(), lua_gc::GcAge::Old);
6531 assert_eq!(table.0.color(), lua_gc::Color::Black);
6532
6533 assert!(state.external_unroot_value(table_key).is_some());
6534 state.gc().full_collect();
6535 }
6536
6537 #[test]
6538 fn gc_packed_params_return_user_visible_values() {
6539 let mut state = new_state().expect("state should initialize");
6540 assert_eq!(
6541 crate::api::gc(&mut state, crate::api::GcArgs::SetPause { value: 200 }),
6542 200
6543 );
6544 assert_eq!(state.global().gc_pause_param(), 200);
6545 assert_eq!(
6546 crate::api::gc(&mut state, crate::api::GcArgs::SetStepMul { value: 200 }),
6547 100
6548 );
6549 assert_eq!(state.global().gc_stepmul_param(), 200);
6550
6551 crate::api::gc(
6552 &mut state,
6553 crate::api::GcArgs::Gen {
6554 minormul: 0,
6555 majormul: 200,
6556 },
6557 );
6558 assert_eq!(state.global().gc_genmajormul_param(), 200);
6559 }
6560
6561 #[test]
6562 fn generational_step_runs_bad_major_when_growth_exceeds_genmajormul() {
6563 let mut state = new_state().expect("state should initialize");
6564 let _heap_guard = {
6565 let g = state.global();
6566 lua_gc::HeapGuard::push(&g.heap)
6567 };
6568
6569 let root = state.new_table();
6570 let root_key = state.external_root_value(LuaValue::Table(root));
6571 state.gc().change_mode(GcKind::Generational);
6572
6573 let root_value = LuaValue::Table(root);
6574 for i in 1..=64 {
6575 let child = state.new_table();
6576 let child_value = LuaValue::Table(child);
6577 root.raw_set_int(&mut state, i, child_value.clone())
6578 .expect("table store should succeed");
6579 state.gc_barrier_back(&root_value, &child_value);
6580 }
6581
6582 {
6583 let mut g = state.global_mut();
6584 g.gc_estimate = 1;
6585 set_debt(&mut *g, 1);
6586 }
6587
6588 assert!(state.gc().generational_step());
6589 let g = state.global();
6590 assert!(g.is_gen_mode());
6591 assert!(
6592 g.lastatomic > 0,
6593 "bad major collection should arm stepgenfull"
6594 );
6595 assert!(g.gc_estimate > 1);
6596 assert!(g.gc_debt() <= 0);
6597 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6598 drop(g);
6599
6600 assert!(state.external_unroot_value(root_key).is_some());
6601 state.gc().full_collect();
6602 }
6603
6604 #[test]
6605 fn generational_implicit_step_runs_major_when_heap_threshold_exceeded() {
6606 let mut state = new_state().expect("state should initialize");
6607 let _heap_guard = {
6608 let g = state.global();
6609 lua_gc::HeapGuard::push(&g.heap)
6610 };
6611
6612 let root = state.new_table();
6613 let root_key = state.external_root_value(LuaValue::Table(root));
6614 state.gc().change_mode(GcKind::Generational);
6615
6616 let root_value = LuaValue::Table(root);
6617 for i in 1..=64 {
6618 let child = state.new_table();
6619 let child_value = LuaValue::Table(child);
6620 root.raw_set_int(&mut state, i, child_value.clone())
6621 .expect("table store should succeed");
6622 state.gc_barrier_back(&root_value, &child_value);
6623 }
6624
6625 {
6626 let mut g = state.global_mut();
6627 g.gc_estimate = 1;
6628 set_debt(&mut *g, -1);
6629 g.heap.set_threshold_bytes(1);
6630 }
6631
6632 assert!(state.gc().generational_step());
6633 let g = state.global();
6634 assert!(g.is_gen_mode());
6635 assert!(
6636 g.lastatomic > 0,
6637 "implicit threshold-triggered growth should arm a bad major"
6638 );
6639 assert!(g.gc_debt() <= 0);
6640 drop(g);
6641
6642 assert!(state.external_unroot_value(root_key).is_some());
6643 state.gc().full_collect();
6644 }
6645
6646 #[test]
6647 fn generational_stepgenfull_returns_to_gen_after_good_collection() {
6648 let mut state = new_state().expect("state should initialize");
6649 let _heap_guard = {
6650 let g = state.global();
6651 lua_gc::HeapGuard::push(&g.heap)
6652 };
6653
6654 let root = state.new_table();
6655 let root_key = state.external_root_value(LuaValue::Table(root));
6656 state.gc().change_mode(GcKind::Generational);
6657 {
6658 let mut g = state.global_mut();
6659 g.lastatomic = 1024;
6660 }
6661
6662 assert!(state.gc().generational_step());
6663 let g = state.global();
6664 assert_eq!(g.gckind, GcKind::Generational as u8);
6665 assert_eq!(g.lastatomic, 0);
6666 assert!(g.gc_debt() <= 0);
6667 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6668 assert_eq!(root.0.color(), lua_gc::Color::Black);
6669 drop(g);
6670
6671 assert!(state.external_unroot_value(root_key).is_some());
6672 state.gc().full_collect();
6673 }
6674
6675 #[test]
6676 fn generational_step_zero_reports_false_without_positive_debt() {
6677 let mut state = new_state().expect("state should initialize");
6678 let _heap_guard = {
6679 let g = state.global();
6680 lua_gc::HeapGuard::push(&g.heap)
6681 };
6682
6683 state.gc().change_mode(GcKind::Generational);
6684 assert_eq!(
6685 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 0 }),
6686 0
6687 );
6688 assert_eq!(
6689 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 1 }),
6690 1
6691 );
6692 }
6693}
6694
6695