1use std::cell::RefCell;
23use std::rc::Rc;
24
25use crate::string::StringPool;
26pub use lua_types::error::LuaError;
27pub use lua_types::{CallInfoIdx, StackIdx};
28
29pub struct StackIdxConv(pub StackIdx);
32
33#[inline(always)]
38pub fn stack_idx_to_i32(i: StackIdx) -> i32 { i.0 as i32 }
39
40impl From<u32> for StackIdxConv {
41 #[inline(always)]
42 fn from(v: u32) -> Self { StackIdxConv(StackIdx(v)) }
43}
44impl From<i32> for StackIdxConv {
45 #[inline(always)]
46 fn from(v: i32) -> Self { StackIdxConv(StackIdx(v.max(0) as u32)) }
47}
48impl From<usize> for StackIdxConv {
49 #[inline(always)]
50 fn from(v: usize) -> Self { StackIdxConv(StackIdx(v as u32)) }
51}
52impl From<StackIdx> for StackIdxConv {
53 #[inline(always)]
54 fn from(v: StackIdx) -> Self { StackIdxConv(v) }
55}
56pub use lua_types::value::{LuaTable, LuaValue, F2Imod};
57pub use lua_types::string::LuaString;
58pub use lua_types::userdata::LuaUserData;
59pub use lua_types::closure::{LuaCFnPtr, LuaClosure, LuaLClosure as LuaClosureLua, LuaCClosure as LuaClosureC};
60pub use lua_types::proto::LuaProto;
61pub use lua_types::upval::{UpVal, UpValState};
62pub use lua_types::gc::GcRef;
63
64pub type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
71
72pub type LuaRustFunction = Rc<dyn Fn(&mut LuaState) -> Result<usize, LuaError>>;
73
74#[derive(Clone)]
75pub enum LuaCallable {
76 Bare(LuaCFunction),
77 Rust(LuaRustFunction),
78}
79
80impl std::fmt::Debug for LuaCallable {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 LuaCallable::Bare(_) => f.write_str("LuaCallable::Bare(..)"),
84 LuaCallable::Rust(_) => f.write_str("LuaCallable::Rust(..)"),
85 }
86 }
87}
88
89impl LuaCallable {
90 pub fn bare(f: LuaCFunction) -> Self {
91 LuaCallable::Bare(f)
92 }
93
94 pub fn rust(f: LuaRustFunction) -> Self {
95 LuaCallable::Rust(f)
96 }
97
98 pub fn as_bare(&self) -> Option<LuaCFunction> {
99 match self {
100 LuaCallable::Bare(f) => Some(*f),
101 LuaCallable::Rust(_) => None,
102 }
103 }
104
105 pub fn call(&self, state: &mut LuaState) -> Result<usize, LuaError> {
106 match self {
107 LuaCallable::Bare(f) => f(state),
108 LuaCallable::Rust(f) => f(state),
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
114pub enum FinalizerObject {
115 Table(GcRef<LuaTable>),
116 UserData(GcRef<LuaUserData>),
117}
118
119impl FinalizerObject {
120 pub fn identity(&self) -> usize {
121 match self {
122 FinalizerObject::Table(t) => t.identity(),
123 FinalizerObject::UserData(u) => u.identity(),
124 }
125 }
126
127 pub fn metatable(&self) -> Option<GcRef<LuaTable>> {
128 match self {
129 FinalizerObject::Table(t) => t.metatable(),
130 FinalizerObject::UserData(u) => u.metatable(),
131 }
132 }
133
134 pub fn as_lua_value(&self) -> LuaValue {
135 match self {
136 FinalizerObject::Table(t) => LuaValue::Table(t.clone()),
137 FinalizerObject::UserData(u) => LuaValue::UserData(u.clone()),
138 }
139 }
140
141 pub fn mark(&self, marker: &mut lua_gc::Marker) {
142 match self {
143 FinalizerObject::Table(t) => marker.mark(t.0),
144 FinalizerObject::UserData(u) => marker.mark(u.0),
145 }
146 }
147
148 pub fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
149 Some(match self {
150 FinalizerObject::Table(t) => t.0.as_trace_ptr(),
151 FinalizerObject::UserData(u) => u.0.as_trace_ptr(),
152 })
153 }
154
155 pub fn age(&self) -> lua_gc::GcAge {
156 match self {
157 FinalizerObject::Table(t) => t.0.age(),
158 FinalizerObject::UserData(u) => u.0.age(),
159 }
160 }
161
162 pub fn is_finalized(&self) -> bool {
163 match self {
164 FinalizerObject::Table(t) => t.0.is_finalized(),
165 FinalizerObject::UserData(u) => u.0.is_finalized(),
166 }
167 }
168
169 pub fn set_finalized(&self, finalized: bool) {
170 match self {
171 FinalizerObject::Table(t) => t.0.set_finalized(finalized),
172 FinalizerObject::UserData(u) => u.0.set_finalized(finalized),
173 }
174 }
175}
176
177impl lua_gc::FinalizerEntry for FinalizerObject {
178 fn identity(&self) -> usize {
179 FinalizerObject::identity(self)
180 }
181
182 fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
183 FinalizerObject::heap_ptr(self)
184 }
185
186 fn age(&self) -> lua_gc::GcAge {
187 FinalizerObject::age(self)
188 }
189
190 fn is_finalized(&self) -> bool {
191 FinalizerObject::is_finalized(self)
192 }
193
194 fn set_finalized(&self, finalized: bool) {
195 FinalizerObject::set_finalized(self, finalized);
196 }
197}
198
199#[derive(Clone, Debug)]
200pub struct WeakTableEntry {
201 table: lua_types::gc::GcWeak<LuaTable>,
202 kind: lua_gc::WeakListKind,
203}
204
205impl WeakTableEntry {
206 pub fn new(table: &GcRef<LuaTable>) -> Self {
207 let mode = table.weak_mode();
208 let weak_keys = (mode & (1 << 0)) != 0;
209 let weak_values = (mode & (1 << 1)) != 0;
210 let kind = match (weak_keys, weak_values) {
211 (true, true) => lua_gc::WeakListKind::AllWeak,
212 (true, false) => lua_gc::WeakListKind::Ephemeron,
213 (false, true) => lua_gc::WeakListKind::WeakValues,
214 (false, false) => lua_gc::WeakListKind::WeakValues,
215 };
216 Self { table: table.downgrade(), kind }
217 }
218}
219
220impl lua_gc::WeakEntry for WeakTableEntry {
221 type Strong = GcRef<LuaTable>;
222
223 fn identity(&self) -> usize {
224 self.table.identity()
225 }
226
227 fn list_kind(&self) -> lua_gc::WeakListKind {
228 self.kind
229 }
230
231 fn upgrade(&self) -> Option<Self::Strong> {
232 self.table.upgrade()
233 }
234}
235
236pub(crate) const EXTRA_STACK: usize = 5;
240
241pub(crate) const LUA_MINSTACK: usize = 20;
243
244pub(crate) const BASIC_STACK_SIZE: usize = 2 * LUA_MINSTACK;
246
247pub(crate) const LUAI_MAXCCALLS: u32 = 200;
267
268pub(crate) const CIST_C: u16 = 1 << 1;
270
271pub(crate) const CIST_OAH: u16 = 1 << 0;
273pub(crate) const CIST_FRESH: u16 = 1 << 2;
274pub(crate) const CIST_HOOKED: u16 = 1 << 3;
275pub(crate) const CIST_YPCALL: u16 = 1 << 4;
276pub(crate) const CIST_TAIL: u16 = 1 << 5;
277pub(crate) const CIST_HOOKYIELD: u16 = 1 << 6;
278pub(crate) const CIST_FIN: u16 = 1 << 7;
279pub(crate) const CIST_TRAN: u16 = 1 << 8;
280pub(crate) const CIST_RECST: u32 = 10;
281pub(crate) const CIST_LEQ: u16 = 1 << 13;
288
289const LUA_NUMTYPES: usize = 9;
291
292const GCSTPUSR: u8 = 1;
294const GCSTPGC: u8 = 2;
295
296const GCS_PAUSE: u8 = 0;
298
299const LUAI_GCPAUSE: u32 = 200;
300const LUAI_GCMUL: u32 = 100;
301const LUAI_GCSTEPSIZE: u8 = 13;
302const LUAI_GENMAJORMUL: u32 = 100;
303const LUAI_GENMINORMUL: u8 = 20;
304
305const WHITE0BIT: u8 = 0;
306
307const STRCACHE_N: usize = 53;
308const STRCACHE_M: usize = 2;
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq)]
316pub enum GcKind {
317 Incremental = 0,
318 Generational = 1,
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
329pub enum WarnMode {
330 Off,
331 On,
332 Cont,
333}
334
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub enum TestWarnMode {
338 Normal,
339 Allow,
340 Store,
341}
342
343pub use lua_types::status::LuaStatus;
348
349#[derive(Clone)]
356pub struct StackValue {
357 pub val: LuaValue,
358 pub tbc_delta: u16,
359}
360
361impl Default for StackValue {
362 fn default() -> Self {
363 StackValue {
364 val: LuaValue::Nil,
365 tbc_delta: 0,
366 }
367 }
368}
369
370#[derive(Clone)]
379pub struct CallInfo {
380 pub func: StackIdx,
382
383 pub top: StackIdx,
385
386 pub previous: Option<CallInfoIdx>,
388
389 pub next: Option<CallInfoIdx>,
391
392 pub u: CallInfoFrame,
393
394 pub u2: CallInfoExtra,
395
396 pub nresults: i16,
398
399 pub callstatus: u16,
401}
402
403#[derive(Clone, Copy)]
406pub enum CallInfoFrame {
407 Lua {
408 savedpc: u32,
410 trap: bool,
412 nextraargs: i32,
414 },
415 C {
416 k: Option<LuaKFunction>,
418 old_errfunc: isize,
420 ctx: isize,
422 },
423}
424
425pub type LuaKFunction = fn(&mut LuaState, status: i32, ctx: isize) -> Result<usize, LuaError>;
427
428#[derive(Default, Clone, Copy)]
432pub struct CallInfoExtra {
433 pub value: i32,
434 pub ftransfer: u16,
435 pub ntransfer: u16,
436}
437
438impl CallInfoFrame {
439 pub fn c_default() -> Self {
441 CallInfoFrame::C {
442 k: None,
443 old_errfunc: 0,
444 ctx: 0,
445 }
446 }
447
448 pub fn lua_default() -> Self {
450 CallInfoFrame::Lua {
451 savedpc: 0,
452 trap: false,
453 nextraargs: 0,
454 }
455 }
456}
457
458impl Default for CallInfo {
459 fn default() -> Self {
460 CallInfo {
461 func: StackIdx(0),
462 top: StackIdx(0),
463 previous: None,
464 next: None,
465 u: CallInfoFrame::c_default(),
466 u2: CallInfoExtra::default(),
467 nresults: 0,
468 callstatus: 0,
469 }
470 }
471}
472
473impl CallInfo {
474 pub fn is_lua(&self) -> bool { (self.callstatus & CIST_C) == 0 }
475 pub fn is_lua_code(&self) -> bool { self.is_lua() }
476 pub fn is_vararg_func(&self) -> bool { false }
483 pub fn saved_pc(&self) -> u32 {
484 if let CallInfoFrame::Lua { savedpc, .. } = self.u { savedpc } else { 0 }
485 }
486 pub fn set_saved_pc(&mut self, pc: u32) {
487 if let CallInfoFrame::Lua { ref mut savedpc, .. } = self.u { *savedpc = pc; }
488 }
489 pub fn nextra_args(&self) -> i32 {
490 if let CallInfoFrame::Lua { nextraargs, .. } = self.u { nextraargs } else { 0 }
491 }
492 pub fn transfer_ftransfer(&self) -> u16 { self.u2.ftransfer }
493 pub fn transfer_ntransfer(&self) -> u16 { self.u2.ntransfer }
494 pub fn set_trap(&mut self, t: bool) {
495 if let CallInfoFrame::Lua { ref mut trap, .. } = self.u { *trap = t; }
496 }
497 pub fn recover_status(&self) -> i32 {
500 ((self.callstatus >> CIST_RECST) & 7) as i32
501 }
502 pub fn set_recover_status<T: Into<i32>>(&mut self, status: T) {
505 let st = (status.into() & 7) as u16;
506 self.callstatus = (self.callstatus & !(7u16 << CIST_RECST)) | (st << CIST_RECST);
507 }
508 pub fn get_oah(&self) -> bool { (self.callstatus & CIST_OAH) != 0 }
509 pub fn set_oah(&mut self, allow: bool) {
512 self.callstatus = (self.callstatus & !CIST_OAH) | (if allow { CIST_OAH } else { 0 });
513 }
514 pub fn u_c_old_errfunc(&self) -> isize {
515 if let CallInfoFrame::C { old_errfunc, .. } = self.u { old_errfunc } else { 0 }
516 }
517 pub fn u_c_ctx(&self) -> isize {
518 if let CallInfoFrame::C { ctx, .. } = self.u { ctx } else { 0 }
519 }
520 pub fn u_c_k(&self) -> Option<LuaKFunction> {
521 if let CallInfoFrame::C { k, .. } = self.u { k } else { None }
522 }
523 pub fn set_u_c_k(&mut self, k: Option<LuaKFunction>) {
527 if let CallInfoFrame::C { k: ref mut slot, .. } = self.u {
528 *slot = k;
529 }
530 }
531 pub fn set_u_c_ctx(&mut self, ctx: isize) {
533 if let CallInfoFrame::C { ctx: ref mut slot, .. } = self.u {
534 *slot = ctx;
535 }
536 }
537 pub fn set_u_c_old_errfunc(&mut self, old_errfunc: isize) {
539 if let CallInfoFrame::C { old_errfunc: ref mut slot, .. } = self.u {
540 *slot = old_errfunc;
541 }
542 }
543 pub fn set_u2_funcidx(&mut self, idx: i32) {
546 self.u2.value = idx;
547 }
548}
549
550pub trait LuaValueExt {
556 fn base_type(&self) -> lua_types::LuaType;
557 fn to_number_no_strconv(&self) -> Option<f64>;
558 fn to_number_with_strconv(&self) -> Option<f64>;
559 fn to_integer_no_strconv(&self) -> Option<i64>;
560 fn to_integer_with_strconv(&self) -> Option<i64>;
561 fn full_type_tag(&self) -> u8;
562}
563
564impl LuaValueExt for LuaValue {
565 fn base_type(&self) -> lua_types::LuaType { self.type_tag() }
566 fn to_number_no_strconv(&self) -> Option<f64> {
567 match self {
568 LuaValue::Float(f) => Some(*f),
569 LuaValue::Int(i) => Some(*i as f64),
570 _ => None,
571 }
572 }
573 fn to_number_with_strconv(&self) -> Option<f64> {
574 if let Some(n) = self.to_number_no_strconv() { return Some(n); }
575 if let LuaValue::Str(s) = self {
576 let mut tmp = LuaValue::Nil;
577 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
578 if sz == 0 { return None; }
579 return match tmp {
580 LuaValue::Int(i) => Some(i as f64),
581 LuaValue::Float(f) => Some(f),
582 _ => None,
583 };
584 }
585 None
586 }
587 fn to_integer_no_strconv(&self) -> Option<i64> {
588 match self {
589 LuaValue::Int(i) => Some(*i),
590 LuaValue::Float(f) if f.fract() == 0.0 && f.is_finite() => {
591 let min_f = i64::MIN as f64;
595 let max_plus1_f = -(i64::MIN as f64);
596 if *f >= min_f && *f < max_plus1_f {
597 Some(*f as i64)
598 } else {
599 None
600 }
601 }
602 _ => None,
603 }
604 }
605 fn to_integer_with_strconv(&self) -> Option<i64> {
606 if let Some(i) = self.to_integer_no_strconv() { return Some(i); }
607 if let LuaValue::Str(s) = self {
608 let mut tmp = LuaValue::Nil;
609 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
610 if sz == 0 { return None; }
611 return tmp.to_integer_no_strconv();
612 }
613 None
614 }
615 fn full_type_tag(&self) -> u8 {
616 match self {
617 LuaValue::Nil => 0x00,
618 LuaValue::Bool(false) => 0x01,
619 LuaValue::Bool(true) => 0x11,
620 LuaValue::Int(_) => 0x03,
621 LuaValue::Float(_) => 0x13,
622 LuaValue::Str(s) if s.is_short() => 0x04,
623 LuaValue::Str(_) => 0x14,
624 LuaValue::LightUserData(_) => 0x02,
625 LuaValue::Table(_) => 0x05,
626 LuaValue::Function(LuaClosure::Lua(_)) => 0x06,
627 LuaValue::Function(LuaClosure::LightC(_)) => 0x16,
628 LuaValue::Function(LuaClosure::C(_)) => 0x26,
629 LuaValue::UserData(_) => 0x07,
630 LuaValue::Thread(_) => 0x08,
631 }
632 }
633}
634
635pub trait LuaTypeExt {
637 fn type_name(&self) -> &'static [u8];
638}
639
640impl LuaTypeExt for lua_types::LuaType {
641 fn type_name(&self) -> &'static [u8] {
642 use lua_types::LuaType::*;
643 match self {
644 None => b"no value",
645 Nil => b"nil",
646 Boolean => b"boolean",
647 LightUserData => b"userdata",
648 Number => b"number",
649 String => b"string",
650 Table => b"table",
651 Function => b"function",
652 UserData => b"userdata",
653 Thread => b"thread",
654 }
655 }
656}
657
658pub trait StackIdxExt {
662 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32;
663 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32;
664 fn raw(self) -> u32;
665}
666impl StackIdxExt for StackIdx {
667 #[inline(always)]
668 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.saturating_sub(n.into().0.0) }
669 #[inline(always)]
670 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.wrapping_sub(n.into().0.0) }
671 #[inline(always)]
672 fn raw(self) -> u32 { self.0 }
673}
674
675pub trait LuaTableRefExt {
685 fn metatable(&self) -> Option<GcRef<LuaTable>>;
686 fn as_ptr(&self) -> *const ();
687 fn get(&self, _k: &LuaValue) -> LuaValue;
688 fn get_int(&self, _k: i64) -> LuaValue;
689 fn get_short_str(&self, _k: &GcRef<LuaString>) -> LuaValue;
690 fn raw_set(&self, _state: &mut LuaState, _k: LuaValue, _v: LuaValue) -> Result<(), LuaError>;
691 fn raw_set_int(&self, _state: &mut LuaState, _k: i64, _v: LuaValue) -> Result<(), LuaError>;
692 fn invalidate_tm_cache(&self);
693 fn resize(&self, _state: &mut LuaState, _na: usize, _nh: usize) -> Result<(), LuaError>;
694 fn next(&self, _k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError>;
695}
696impl LuaTableRefExt for GcRef<LuaTable> {
697 #[inline]
698 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
699 #[inline]
700 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
701 #[inline]
702 fn get(&self, k: &LuaValue) -> LuaValue { (**self).get(k) }
703 #[inline]
704 fn get_int(&self, k: i64) -> LuaValue { (**self).get_int(k) }
705 #[inline]
706 fn get_short_str(&self, k: &GcRef<LuaString>) -> LuaValue { (**self).get_short_str(k) }
707 #[inline]
710 fn raw_set(&self, _state: &mut LuaState, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
711 let before = (**self).buffer_bytes();
712 let result = (**self).try_raw_set(k, v);
713 if result.is_ok() {
714 account_table_buffer_delta(self, before);
715 }
716 result
717 }
718 #[inline]
719 fn raw_set_int(&self, _state: &mut LuaState, k: i64, v: LuaValue) -> Result<(), LuaError> {
720 let before = (**self).buffer_bytes();
721 let result = (**self).try_raw_set_int(k, v);
722 if result.is_ok() {
723 account_table_buffer_delta(self, before);
724 }
725 result
726 }
727 fn invalidate_tm_cache(&self) {}
728 fn resize(&self, _state: &mut LuaState, na: usize, nh: usize) -> Result<(), LuaError> {
729 let before = (**self).buffer_bytes();
730 let na32 = na.min(u32::MAX as usize) as u32;
731 let nh32 = nh.min(u32::MAX as usize) as u32;
732 let result = (**self).resize(na32, nh32);
733 if result.is_ok() {
734 account_table_buffer_delta(self, before);
735 }
736 result
737 }
738 fn next(&self, k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError> {
739 (**self).try_next_pair(&k)
740 }
741}
742
743#[inline]
744fn account_table_buffer_delta(t: &GcRef<LuaTable>, before: usize) {
745 let after = (**t).buffer_bytes();
746 if after > before {
747 t.account_buffer((after - before) as isize);
748 } else if before > after {
749 t.account_buffer(-((before - after) as isize));
750 }
751}
752
753pub trait LuaUserDataRefExt {
754 fn metatable(&self) -> Option<GcRef<LuaTable>>;
755 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>);
756 fn as_ptr(&self) -> *const ();
757 fn len(&self) -> usize;
758}
759impl LuaUserDataRefExt for GcRef<LuaUserData> {
760 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
761 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>) { (**self).set_metatable(mt); }
762 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
763 fn len(&self) -> usize { self.0.data.len() }
764}
765
766pub trait LuaStringRefExt {
767 fn is_white(&self) -> bool;
768 fn hash(&self) -> u32;
769 fn as_gc_ref(&self) -> GcRef<LuaString>;
770}
771impl LuaStringRefExt for GcRef<LuaString> {
772 fn is_white(&self) -> bool { false }
773 fn hash(&self) -> u32 { self.0.hash() }
774 fn as_gc_ref(&self) -> GcRef<LuaString> { self.clone() }
775}
776
777pub trait LuaLClosureRefExt {
778 fn proto(&self) -> &GcRef<LuaProto>;
779 fn nupvalues(&self) -> usize;
780}
781impl LuaLClosureRefExt for GcRef<lua_types::closure::LuaLClosure> {
782 fn proto(&self) -> &GcRef<LuaProto> { &self.0.proto }
783 fn nupvalues(&self) -> usize { self.0.upvals.len() }
784}
785
786pub trait LuaClosureExt {
788 fn nupvalues(&self) -> usize;
789}
790impl LuaClosureExt for LuaClosure {
791 fn nupvalues(&self) -> usize {
792 match self {
793 LuaClosure::Lua(l) => l.0.upvals.len(),
794 LuaClosure::C(c) => c.0.upvalues.borrow().len(),
795 LuaClosure::LightC(_) => 0,
796 }
797 }
798}
799
800pub trait LuaProtoExt {
802 fn source_bytes(&self) -> &[u8];
803 fn source_string(&self) -> Option<&GcRef<LuaString>>;
804}
805impl LuaProtoExt for LuaProto {
806 fn source_bytes(&self) -> &[u8] {
807 match &self.source { Some(s) => s.0.as_bytes(), None => &[] }
808 }
809 fn source_string(&self) -> Option<&GcRef<LuaString>> { self.source.as_ref() }
810}
811
812pub trait Collectable: std::fmt::Debug {}
819
820impl std::fmt::Debug for LuaState {
821 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
822 write!(f, "LuaState")
823 }
824}
825impl Collectable for LuaState {}
826
827pub type ParserHook = fn(
836 state: &mut LuaState,
837 source: &[u8],
838 name: &[u8],
839 firstchar: i32,
840) -> Result<GcRef<lua_types::closure::LuaLClosure>, LuaError>;
841
842pub type FileLoaderHook = fn(filename: &[u8]) -> Result<Vec<u8>, LuaError>;
850
851pub type FileOpenHook =
862 fn(filename: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
863
864pub type OutputHook = fn(bytes: &[u8]) -> std::io::Result<()>;
872
873pub type InputHook = fn(buf: &mut [u8]) -> std::io::Result<usize>;
876
877pub type EnvHook = fn(name: &[u8]) -> Option<Vec<u8>>;
883
884pub type UnixTimeHook = fn() -> i64;
886
887pub type CpuClockHook = fn() -> f64;
895
896pub type LocalOffsetHook = fn(timestamp: i64) -> i64;
908
909pub type EntropyHook = fn() -> u64;
913
914pub type TempNameHook = fn() -> Result<Vec<u8>, LuaError>;
919
920pub type PopenHook =
931 fn(cmd: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
932
933pub type FileRemoveHook = fn(filename: &[u8]) -> Result<(), LuaError>;
939
940pub type FileRenameHook = fn(from: &[u8], to: &[u8]) -> Result<(), LuaError>;
946
947#[derive(Clone, Copy, Debug)]
953pub enum OsExecuteReason {
954 Exit,
956 Signal,
958}
959
960#[derive(Debug)]
963pub struct OsExecuteResult {
964 pub success: bool,
966 pub reason: OsExecuteReason,
968 pub code: i32,
970}
971
972pub type OsExecuteHook = fn(cmd: &[u8]) -> Result<OsExecuteResult, LuaError>;
979
980#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
987pub struct DynLibId(pub u64);
988
989pub enum DynamicSymbol {
997 RustNative(LuaCFunction),
1000 LuaCAbi(*const ()),
1006 Unsupported { reason: Vec<u8> },
1009}
1010
1011pub type DynLibLoadHook =
1022 fn(state: &mut LuaState, path: &[u8], see_global: bool) -> Result<DynLibId, LuaError>;
1023
1024pub type DynLibSymbolHook =
1032 fn(state: &mut LuaState, handle: DynLibId, symbol: &[u8]) -> Result<DynamicSymbol, LuaError>;
1033
1034pub type DynLibUnloadHook = fn(handle: DynLibId);
1042
1043pub struct ThreadRegistryEntry {
1049 pub state: Rc<RefCell<LuaState>>,
1054 pub value: GcRef<lua_types::value::LuaThread>,
1057}
1058
1059#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1064pub struct ExternalRootKey {
1065 index: usize,
1066 generation: u64,
1067}
1068
1069#[derive(Debug)]
1070struct ExternalRootSlot {
1071 value: Option<LuaValue>,
1072 generation: u64,
1073}
1074
1075#[derive(Debug, Default)]
1081pub struct ExternalRootSet {
1082 slots: Vec<ExternalRootSlot>,
1083 free: Vec<usize>,
1084 live: usize,
1085}
1086
1087impl ExternalRootSet {
1088 pub fn insert(&mut self, value: LuaValue) -> ExternalRootKey {
1089 if let Some(index) = self.free.pop() {
1090 let slot = &mut self.slots[index];
1091 debug_assert!(
1092 slot.value.is_none(),
1093 "free external-root slot is occupied"
1094 );
1095 slot.generation = slot.generation.wrapping_add(1).max(1);
1096 slot.value = Some(value);
1097 self.live += 1;
1098 ExternalRootKey {
1099 index,
1100 generation: slot.generation,
1101 }
1102 } else {
1103 let index = self.slots.len();
1104 self.slots.push(ExternalRootSlot {
1105 value: Some(value),
1106 generation: 1,
1107 });
1108 self.live += 1;
1109 ExternalRootKey {
1110 index,
1111 generation: 1,
1112 }
1113 }
1114 }
1115
1116 pub fn get(&self, key: ExternalRootKey) -> Option<&LuaValue> {
1117 let slot = self.slots.get(key.index)?;
1118 if slot.generation == key.generation {
1119 slot.value.as_ref()
1120 } else {
1121 None
1122 }
1123 }
1124
1125 pub fn replace(&mut self, key: ExternalRootKey, value: LuaValue) -> Option<LuaValue> {
1126 let slot = self.slots.get_mut(key.index)?;
1127 if slot.generation != key.generation || slot.value.is_none() {
1128 return None;
1129 }
1130 slot.value.replace(value)
1131 }
1132
1133 pub fn remove(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
1134 let slot = self.slots.get_mut(key.index)?;
1135 if slot.generation != key.generation {
1136 return None;
1137 }
1138 let old = slot.value.take()?;
1139 self.free.push(key.index);
1140 self.live -= 1;
1141 Some(old)
1142 }
1143
1144 pub fn iter_values(&self) -> impl Iterator<Item = &LuaValue> {
1145 self.slots.iter().filter_map(|slot| slot.value.as_ref())
1146 }
1147
1148 pub fn len(&self) -> usize {
1149 self.live
1150 }
1151
1152 pub fn is_empty(&self) -> bool {
1153 self.live == 0
1154 }
1155
1156 pub fn vacant_len(&self) -> usize {
1157 self.free.len()
1158 }
1159}
1160
1161pub struct GlobalState {
1167 pub parser_hook: Option<ParserHook>,
1173
1174 pub cli_argv: Option<Vec<Vec<u8>>>,
1181
1182 pub cli_preload: Option<fn(&mut LuaState) -> Result<(), LuaError>>,
1186
1187 pub lua_version: lua_types::LuaVersion,
1194
1195 pub file_loader_hook: Option<FileLoaderHook>,
1200
1201 pub file_open_hook: Option<FileOpenHook>,
1206
1207 pub stdout_hook: Option<OutputHook>,
1211
1212 pub stderr_hook: Option<OutputHook>,
1214
1215 pub stdin_hook: Option<InputHook>,
1218
1219 pub env_hook: Option<EnvHook>,
1221
1222 pub unix_time_hook: Option<UnixTimeHook>,
1225
1226 pub cpu_clock_hook: Option<CpuClockHook>,
1229
1230 pub local_offset_hook: Option<LocalOffsetHook>,
1235
1236 pub entropy_hook: Option<EntropyHook>,
1239
1240 pub temp_name_hook: Option<TempNameHook>,
1242
1243 pub popen_hook: Option<PopenHook>,
1248
1249 pub file_remove_hook: Option<FileRemoveHook>,
1252
1253 pub file_rename_hook: Option<FileRenameHook>,
1256
1257 pub os_execute_hook: Option<OsExecuteHook>,
1261
1262 pub dynlib_load_hook: Option<DynLibLoadHook>,
1267
1268 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
1272
1273 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
1277
1278 pub sandbox: SandboxLimits,
1281
1282 pub gc_debt: isize,
1284
1285 pub gc_estimate: usize,
1286
1287 pub lastatomic: usize,
1289
1290 pub strt: StringPool,
1292
1293 pub l_registry: LuaValue,
1295
1296 pub external_roots: ExternalRootSet,
1299
1300 pub globals: LuaValue,
1307 pub loaded: LuaValue,
1308
1309 pub nilvalue: LuaValue,
1313
1314 pub seed: u32,
1316
1317 pub currentwhite: u8,
1319
1320 pub gcstate: u8,
1321
1322 pub gckind: u8,
1323
1324 pub gcstopem: bool,
1325
1326 pub genminormul: u8,
1328
1329 pub genmajormul: u8,
1330
1331 pub gcstp: u8,
1332
1333 pub gcemergency: bool,
1334
1335 pub gcpause: u8,
1337
1338 pub gcstepmul: u8,
1340
1341 pub gcstepsize: u8,
1342
1343 pub gc55_params: [i64; 6],
1352
1353 pub sweepgc_cursor: usize,
1358
1359 pub weak_tables_registry: lua_gc::WeakRegistry<WeakTableEntry>,
1368
1369 pub finalizers: lua_gc::FinalizerRegistry<FinalizerObject>,
1372
1373 pub gc_finalizer_error: Option<LuaValue>,
1384
1385 pub twups: Vec<GcRef<LuaState>>,
1395
1396 pub panic: Option<LuaCFunction>,
1398
1399 pub mainthread: Option<GcRef<LuaState>>,
1402
1403 pub threads: std::collections::HashMap<u64, ThreadRegistryEntry>,
1416
1417 pub main_thread_value: GcRef<lua_types::value::LuaThread>,
1422
1423 pub current_thread_id: u64,
1428
1429 pub main_thread_id: u64,
1432
1433 pub next_thread_id: u64,
1436
1437 pub memerrmsg: GcRef<LuaString>,
1439
1440 pub tmname: Vec<GcRef<LuaString>>,
1443
1444 pub mt: [Option<GcRef<LuaTable>>; LUA_NUMTYPES],
1446
1447 pub strcache: [[GcRef<LuaString>; STRCACHE_M]; STRCACHE_N],
1449
1450 pub interned_lt: std::collections::HashMap<Box<[u8]>, GcRef<LuaString>>,
1456
1457 pub warnf: Option<Box<dyn FnMut(&[u8], bool)>>,
1459
1460 pub warn_mode: WarnMode,
1465
1466 pub test_warn_enabled: bool,
1471 pub test_warn_on: bool,
1472 pub test_warn_mode: TestWarnMode,
1473 pub test_warn_last_to_cont: bool,
1474 pub test_warn_buffer: Vec<u8>,
1475
1476 pub c_functions: Vec<LuaCallable>,
1481
1482 pub heap: lua_gc::Heap,
1487
1488 pub cross_thread_upvals: std::collections::HashMap<(u64, StackIdx), LuaValue>,
1502
1503 pub suspended_parent_stacks: Vec<Vec<LuaValue>>,
1517
1518 pub suspended_parent_open_upvals: Vec<Vec<GcRef<UpVal>>>,
1524}
1525
1526const SANDBOX_COUNT_MASK: u8 = 1 << 3;
1529
1530pub const SANDBOX_TRIP_NONE: u8 = 0;
1532pub const SANDBOX_TRIP_INSTRUCTIONS: u8 = 1;
1534pub const SANDBOX_TRIP_MEMORY: u8 = 2;
1536
1537#[derive(Default)]
1544pub struct SandboxLimits {
1545 pub interval: std::cell::Cell<i32>,
1547 pub instr_limited: std::cell::Cell<bool>,
1549 pub instr_remaining: std::cell::Cell<u64>,
1551 pub instr_limit: std::cell::Cell<u64>,
1553 pub mem_limit: std::cell::Cell<Option<usize>>,
1555 pub tripped: std::cell::Cell<u8>,
1557 pub aborting: std::cell::Cell<bool>,
1562}
1563
1564impl GlobalState {
1565 pub fn sandbox_active(&self) -> bool {
1567 self.sandbox.interval.get() != 0
1568 }
1569
1570 pub fn total_bytes(&self) -> usize {
1575 self.heap.bytes_used().max(1)
1576 }
1577
1578 pub fn get_thread(&self, id: u64) -> Option<&ThreadRegistryEntry> {
1583 self.threads.get(&id)
1584 }
1585
1586 pub fn thread_value_for(&self, id: u64) -> Option<GcRef<lua_types::value::LuaThread>> {
1590 if id == self.main_thread_id {
1591 Some(self.main_thread_value.clone())
1592 } else {
1593 self.threads.get(&id).map(|e| e.value.clone())
1594 }
1595 }
1596
1597 pub fn is_complete(&self) -> bool {
1604 matches!(self.nilvalue, LuaValue::Nil)
1605 }
1606
1607 pub fn current_white(&self) -> u8 {
1615 self.currentwhite
1616 }
1617
1618 pub fn other_white(&self) -> u8 {
1622 self.currentwhite ^ 0x03
1623 }
1624
1625 pub fn is_gen_mode(&self) -> bool {
1629 self.gckind == GcKind::Generational as u8 || self.lastatomic != 0
1630 }
1631
1632 pub fn gc_running(&self) -> bool {
1636 self.gcstp == 0
1637 }
1638
1639 pub fn keep_invariant(&self) -> bool {
1643 self.heap.gc_state().is_invariant()
1644 }
1645
1646 pub fn is_sweep_phase(&self) -> bool {
1650 self.heap.gc_state().is_sweep()
1651 }
1652
1653 pub fn gc_debt(&self) -> isize { self.gc_debt }
1655 pub fn set_gc_debt(&mut self, d: isize) { self.gc_debt = d; }
1656 pub fn gc_at_pause(&self) -> bool { self.heap.gc_state().is_pause() }
1657 fn get_gc_param(p: u8) -> i32 { (p as i32) * 4 }
1658 fn set_gc_param_slot(slot: &mut u8, p: i32) { *slot = (p / 4) as u8; }
1659 pub fn gc_pause_param(&self) -> i32 { Self::get_gc_param(self.gcpause) }
1660 pub fn set_gc_pause_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcpause, p); }
1661 pub fn gc_stepmul_param(&self) -> i32 { Self::get_gc_param(self.gcstepmul) }
1662 pub fn set_gc_stepmul_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcstepmul, p); }
1663 pub fn gc_genmajormul_param(&self) -> i32 { Self::get_gc_param(self.genmajormul) }
1664 pub fn set_gc_genmajormul(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.genmajormul, p); }
1665 pub fn gc55_param(&mut self, idx: usize, value: i64) -> i64 {
1669 let old = self.gc55_params[idx];
1670 if value >= 0 {
1671 self.gc55_params[idx] = value;
1672 }
1673 old
1674 }
1675 pub fn gc_stop_flags(&self) -> u8 { self.gcstp }
1676 pub fn set_gc_stop_flags(&mut self, f: u8) { self.gcstp = f; }
1677 pub fn stop_gc_internal(&mut self) -> u8 {
1678 let old = self.gcstp;
1679 self.gcstp |= GCSTPGC;
1680 old
1681 }
1682 pub fn set_gc_stop_user(&mut self) {
1683 self.gcstp = GCSTPUSR;
1685 }
1686 pub fn clear_gc_stop(&mut self) { self.gcstp = 0; }
1687 pub fn is_gc_running(&self) -> bool { self.gcstp == 0 }
1688 pub fn is_gc_stopped_internally(&self) -> bool { (self.gcstp & GCSTPGC) != 0 }
1693
1694 pub fn tm_name<T: TmIndex>(&self, tm: T) -> Option<GcRef<LuaString>> {
1704 self.tmname.get(tm.tm_index()).cloned()
1705 }
1706}
1707
1708pub trait TmIndex: Copy {
1714 fn tm_index(self) -> usize;
1715}
1716impl TmIndex for lua_types::tagmethod::TagMethod {
1717 fn tm_index(self) -> usize { self as u8 as usize }
1718}
1719impl TmIndex for crate::tagmethods::TagMethod {
1720 fn tm_index(self) -> usize { self as u8 as usize }
1721}
1722impl TmIndex for usize {
1723 fn tm_index(self) -> usize { self }
1724}
1725impl TmIndex for u8 {
1726 fn tm_index(self) -> usize { self as usize }
1727}
1728
1729use lua_types::tagmethod::TagMethod;
1730
1731pub struct LuaState {
1741 pub status: u8,
1745
1746 pub allowhook: bool,
1748
1749 pub nci: u32,
1751
1752 pub top: StackIdx,
1756
1757 pub stack_last: StackIdx,
1759
1760 pub stack: Vec<StackValue>,
1762
1763 pub ci: CallInfoIdx,
1767
1768 pub call_info: Vec<CallInfo>,
1771
1772 pub openupval: Vec<GcRef<UpVal>>,
1776
1777 pub tbclist: Vec<StackIdx>,
1779
1780 pub(crate) global: Rc<RefCell<GlobalState>>,
1785
1786 pub hook: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>,
1790
1791 pub hookmask: u8,
1793
1794 pub basehookcount: i32,
1796
1797 pub hookcount: i32,
1799
1800 pub errfunc: isize,
1807
1808 pub n_ccalls: u32,
1812
1813 pub oldpc: u32,
1817
1818 pub marked: u8,
1822
1823 pub cached_thread_id: u64,
1839
1840 pub gc_check_needed: bool,
1845
1846}
1847
1848impl LuaState {
1849 pub fn global(&self) -> std::cell::Ref<'_, GlobalState> {
1857 self.global.borrow()
1858 }
1859
1860 pub fn global_mut(&self) -> std::cell::RefMut<'_, GlobalState> {
1864 self.global.borrow_mut()
1865 }
1866
1867 pub fn global_rc(&self) -> Rc<RefCell<GlobalState>> {
1871 Rc::clone(&self.global)
1872 }
1873
1874 pub fn enable_test_warning_handler(&mut self) -> Result<(), LuaError> {
1876 {
1877 let mut g = self.global_mut();
1878 g.test_warn_enabled = true;
1879 g.test_warn_on = false;
1880 g.test_warn_mode = TestWarnMode::Normal;
1881 g.test_warn_last_to_cont = false;
1882 g.test_warn_buffer.clear();
1883 }
1884 self.push(LuaValue::Bool(false));
1885 crate::api::set_global(self, b"_WARN")
1886 }
1887
1888 pub fn c_calls(&self) -> u32 {
1892 self.n_ccalls & 0xffff
1893 }
1894
1895 pub fn inc_nny(&mut self) {
1899 self.n_ccalls += 0x10000;
1900 }
1901
1902 pub fn dec_nny(&mut self) {
1906 self.n_ccalls -= 0x10000;
1907 }
1908
1909 pub fn is_yieldable(&self) -> bool {
1913 (self.n_ccalls & 0xffff0000) == 0
1914 }
1915
1916 pub fn reset_hook_count(&mut self) {
1920 self.hookcount = self.basehookcount;
1921 }
1922
1923 pub fn install_sandbox_limits(
1932 &mut self,
1933 interval: i32,
1934 instr_limit: Option<u64>,
1935 mem_limit: Option<usize>,
1936 ) {
1937 let interval = interval.max(1);
1938 {
1939 let g = self.global();
1940 g.sandbox.interval.set(interval);
1941 g.sandbox.instr_limited.set(instr_limit.is_some());
1942 g.sandbox.instr_remaining.set(instr_limit.unwrap_or(0));
1943 g.sandbox.instr_limit.set(instr_limit.unwrap_or(0));
1944 g.sandbox.mem_limit.set(mem_limit);
1945 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
1946 }
1947 self.hookmask |= SANDBOX_COUNT_MASK;
1948 self.basehookcount = interval;
1949 self.hookcount = interval;
1950 crate::debug::arm_traps(self);
1951 }
1952
1953 pub fn sandbox_charge_interval(&self) -> Option<LuaError> {
1958 let interval = self.global().sandbox.interval.get();
1959 self.sandbox_charge(interval as u64)
1960 }
1961
1962 pub fn sandbox_charge(&self, amount: u64) -> Option<LuaError> {
1971 let g = self.global();
1972 if g.sandbox.interval.get() == 0 {
1973 return None;
1974 }
1975 if g.sandbox.instr_limited.get() {
1976 let rem = g.sandbox.instr_remaining.get().saturating_sub(amount);
1977 g.sandbox.instr_remaining.set(rem);
1978 if rem == 0 {
1979 g.sandbox.tripped.set(SANDBOX_TRIP_INSTRUCTIONS);
1980 g.sandbox.aborting.set(true);
1981 return Some(LuaError::runtime(format_args!(
1982 "sandbox: instruction budget exhausted"
1983 )));
1984 }
1985 }
1986 if let Some(limit) = g.sandbox.mem_limit.get() {
1987 if g.total_bytes() > limit {
1988 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
1989 g.sandbox.aborting.set(true);
1990 return Some(LuaError::runtime(format_args!(
1991 "sandbox: memory limit exceeded"
1992 )));
1993 }
1994 }
1995 None
1996 }
1997
1998 pub fn sandbox_reserve(&self, additional: usize) -> Option<LuaError> {
2006 let g = self.global();
2007 if g.sandbox.interval.get() == 0 {
2008 return None;
2009 }
2010 if let Some(limit) = g.sandbox.mem_limit.get() {
2011 let projected = g.total_bytes().saturating_add(additional);
2012 if projected > limit {
2013 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2014 g.sandbox.aborting.set(true);
2015 return Some(LuaError::runtime(format_args!(
2016 "sandbox: memory limit exceeded"
2017 )));
2018 }
2019 }
2020 None
2021 }
2022
2023 pub fn sandbox_match_step_limit(&self) -> u64 {
2028 let g = self.global();
2029 if g.sandbox.interval.get() != 0 && g.sandbox.instr_limited.get() {
2030 g.sandbox.instr_remaining.get()
2031 } else {
2032 0
2033 }
2034 }
2035
2036 pub fn sandbox_aborting(&self) -> bool {
2040 self.global().sandbox.aborting.get()
2041 }
2042
2043 pub fn sandbox_instr_limited(&self) -> bool {
2045 self.global().sandbox.instr_limited.get()
2046 }
2047
2048 pub fn sandbox_instr_remaining(&self) -> u64 {
2051 self.global().sandbox.instr_remaining.get()
2052 }
2053
2054 pub fn sandbox_instr_limit(&self) -> u64 {
2056 self.global().sandbox.instr_limit.get()
2057 }
2058
2059 pub fn sandbox_tripped_code(&self) -> u8 {
2061 self.global().sandbox.tripped.get()
2062 }
2063
2064 pub fn sandbox_reset(&self) {
2067 let g = self.global();
2068 if g.sandbox.instr_limited.get() {
2069 g.sandbox.instr_remaining.set(g.sandbox.instr_limit.get());
2070 }
2071 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2072 g.sandbox.aborting.set(false);
2073 }
2074
2075 pub fn stack_size(&self) -> usize {
2079 self.stack_last.0 as usize
2080 }
2081
2082 #[inline(always)]
2086 pub fn push(&mut self, val: LuaValue) {
2087 let top = self.top.0 as usize;
2088 if top < self.stack.len() {
2089 self.stack[top] = StackValue { val, tbc_delta: 0 };
2090 } else {
2091 self.stack.push(StackValue { val, tbc_delta: 0 });
2092 }
2093 self.top = StackIdx(self.top.0 + 1);
2094 }
2095
2096 #[inline(always)]
2099 pub fn pop(&mut self) -> LuaValue {
2100 if self.top.0 == 0 {
2101 return LuaValue::Nil;
2102 }
2103 self.top = StackIdx(self.top.0 - 1);
2104 self.stack[self.top.0 as usize].val.clone()
2105 }
2106
2107 #[inline(always)]
2111 pub fn stack_val(&self, idx: StackIdx) -> &LuaValue {
2112 &self.stack[idx.0 as usize].val
2113 }
2114
2115 #[inline(always)]
2117 pub fn set_stack_val(&mut self, idx: StackIdx, val: LuaValue) {
2118 self.stack[idx.0 as usize].val = val;
2119 }
2120
2121 pub fn gc(&mut self) -> GcHandle<'_> {
2128 GcHandle { _state: self }
2129 }
2130
2131 pub fn external_root_value(&mut self, value: LuaValue) -> ExternalRootKey {
2133 self.global_mut().external_roots.insert(value)
2134 }
2135
2136 pub fn external_rooted_value(&self, key: ExternalRootKey) -> Option<LuaValue> {
2138 self.global().external_roots.get(key).cloned()
2139 }
2140
2141 pub fn external_replace_root(
2143 &mut self,
2144 key: ExternalRootKey,
2145 value: LuaValue,
2146 ) -> Option<LuaValue> {
2147 self.global_mut().external_roots.replace(key, value)
2148 }
2149
2150 pub fn external_unroot_value(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
2152 self.global_mut().external_roots.remove(key)
2153 }
2154
2155 pub fn try_external_unroot_value(
2158 &mut self,
2159 key: ExternalRootKey,
2160 ) -> std::result::Result<Option<LuaValue>, std::cell::BorrowMutError> {
2161 self.global
2162 .try_borrow_mut()
2163 .map(|mut global| global.external_roots.remove(key))
2164 }
2165
2166 pub fn new_table(&mut self) -> GcRef<LuaTable> {
2170 self.mark_gc_check_needed();
2172 GcRef::new(LuaTable::placeholder())
2173 }
2174
2175 pub fn new_table_with_sizes(
2180 &mut self,
2181 array_size: u32,
2182 hash_size: u32,
2183 ) -> Result<GcRef<LuaTable>, LuaError> {
2184 self.mark_gc_check_needed();
2185 let t = GcRef::new(LuaTable::placeholder());
2186 self.table_resize(&t, array_size as usize, hash_size as usize)?;
2187 Ok(t)
2188 }
2189
2190 pub fn intern_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2201 if bytes.len() <= crate::string::MAX_SHORT_LEN {
2202 if let Some(existing) = self.global().interned_lt.get(bytes) {
2203 return Ok(existing.clone());
2204 }
2205 self.mark_gc_check_needed();
2206 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2207 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2208 self.global_mut()
2209 .interned_lt
2210 .insert(bytes.to_vec().into_boxed_slice(), new_ref.clone());
2211 Ok(new_ref)
2212 } else {
2213 self.mark_gc_check_needed();
2214 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2215 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2216 Ok(new_ref)
2217 }
2218 }
2219
2220 #[inline(always)]
2222 pub fn top_idx(&self) -> StackIdx {
2223 self.top
2224 }
2225}
2226
2227impl LuaState {
2236 #[inline(always)]
2237 pub fn get_at(&self, idx: impl Into<StackIdxConv>) -> LuaValue {
2238 let i: StackIdx = idx.into().0;
2239 match self.stack.get(i.0 as usize) {
2240 Some(slot) => slot.val.clone(),
2241 None => LuaValue::Nil,
2242 }
2243 }
2244 #[inline(always)]
2245 pub fn set_at(&mut self, idx: impl Into<StackIdxConv>, v: LuaValue) {
2246 let i: StackIdx = idx.into().0;
2247 self.stack[i.0 as usize].val = v;
2248 }
2249
2250 pub fn clear_stack_range(&mut self, start: StackIdx, end: StackIdx) {
2256 if end.0 <= start.0 {
2257 return;
2258 }
2259 let end_u = end.0 as usize;
2260 if end_u > self.stack.len() {
2261 self.stack.resize_with(end_u, StackValue::default);
2262 }
2263 for i in start.0..end.0 {
2264 self.stack[i as usize].val = LuaValue::Nil;
2265 self.stack[i as usize].tbc_delta = 0;
2266 }
2267 }
2268 #[inline(always)]
2276 pub fn get_int_at(&self, idx: impl Into<StackIdxConv>) -> Option<i64> {
2277 let i: StackIdx = idx.into().0;
2278 match self.stack.get(i.0 as usize) {
2279 Some(slot) => match &slot.val {
2280 LuaValue::Int(v) => Some(*v),
2281 _ => None,
2282 },
2283 None => None,
2284 }
2285 }
2286 #[inline(always)]
2293 pub fn get_int_pair_at(
2294 &self,
2295 rb: impl Into<StackIdxConv>,
2296 rc: impl Into<StackIdxConv>,
2297 ) -> Option<(i64, i64)> {
2298 let rb: StackIdx = rb.into().0;
2299 let rc: StackIdx = rc.into().0;
2300 match (
2301 self.stack[rb.0 as usize].val,
2302 self.stack[rc.0 as usize].val,
2303 ) {
2304 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib, ic)),
2305 _ => None,
2306 }
2307 }
2308 #[inline(always)]
2313 pub fn get_num_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2314 let i: StackIdx = idx.into().0;
2315 match self.stack.get(i.0 as usize) {
2316 Some(slot) => match &slot.val {
2317 LuaValue::Float(f) => Some(*f),
2318 LuaValue::Int(v) => Some(*v as f64),
2319 _ => None,
2320 },
2321 None => None,
2322 }
2323 }
2324 #[inline(always)]
2329 pub fn get_float_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2330 let i: StackIdx = idx.into().0;
2331 match self.stack.get(i.0 as usize) {
2332 Some(slot) => match &slot.val {
2333 LuaValue::Float(f) => Some(*f),
2334 _ => None,
2335 },
2336 None => None,
2337 }
2338 }
2339 #[inline(always)]
2344 pub fn get_num_pair_at(
2345 &self,
2346 rb: impl Into<StackIdxConv>,
2347 rc: impl Into<StackIdxConv>,
2348 ) -> Option<(f64, f64)> {
2349 let rb: StackIdx = rb.into().0;
2350 let rc: StackIdx = rc.into().0;
2351 match (
2352 self.stack[rb.0 as usize].val,
2353 self.stack[rc.0 as usize].val,
2354 ) {
2355 (LuaValue::Float(nb), LuaValue::Float(nc)) => Some((nb, nc)),
2356 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib as f64, ic as f64)),
2357 (LuaValue::Int(ib), LuaValue::Float(nc)) => Some((ib as f64, nc)),
2358 (LuaValue::Float(nb), LuaValue::Int(ic)) => Some((nb, ic as f64)),
2359 _ => None,
2360 }
2361 }
2362 #[inline(always)]
2375 pub fn set_top(&mut self, idx: impl Into<StackIdxConv>) {
2376 let new_top: StackIdx = idx.into().0;
2377 let new_top_u = new_top.0 as usize;
2378 if new_top_u > self.stack.len() {
2379 self.stack.resize_with(new_top_u, StackValue::default);
2380 }
2381 self.top = new_top;
2382 }
2383 #[inline(always)]
2389 pub fn set_top_idx(&mut self, idx: impl Into<StackIdxConv>) {
2390 let new_top: StackIdx = idx.into().0;
2391 self.top = new_top;
2392 }
2393 #[inline(always)]
2396 pub fn dec_top(&mut self) {
2397 if self.top.0 > 0 {
2398 self.top = StackIdx(self.top.0 - 1);
2399 }
2400 }
2401 #[inline(always)]
2402 pub fn pop_n(&mut self, n: usize) {
2403 let cur = self.top.0 as usize;
2404 let new = cur.saturating_sub(n);
2405 self.top = StackIdx(new as u32);
2406 }
2407 #[inline(always)]
2410 pub fn peek_at(&mut self, idx: impl Into<StackIdxConv>) -> LuaValue {
2411 let i: StackIdx = idx.into().0;
2412 match self.stack.get(i.0 as usize) {
2413 Some(slot) => slot.val.clone(),
2414 None => LuaValue::Nil,
2415 }
2416 }
2417 #[inline(always)]
2421 pub fn peek_top(&mut self) -> LuaValue {
2422 if self.top.0 == 0 {
2423 return LuaValue::Nil;
2424 }
2425 self.stack[(self.top.0 - 1) as usize].val.clone()
2426 }
2427 pub fn peek_string_at_top(&mut self) -> GcRef<LuaString> {
2432 match self.peek_top() {
2433 LuaValue::Str(s) => s,
2434 _ => panic!("peek_string_at_top: top of stack is not a string"),
2435 }
2436 }
2437 pub fn stack_at(&mut self, idx: impl Into<StackIdxConv>) -> &mut LuaValue {
2440 let i: StackIdx = idx.into().0;
2441 &mut self.stack[i.0 as usize].val
2442 }
2443 pub fn stack_set_nil(&mut self, idx: impl Into<StackIdxConv>) {
2446 let i: StackIdx = idx.into().0;
2447 let slot = i.0 as usize;
2448 if slot < self.stack.len() {
2449 self.stack[slot].val = LuaValue::Nil;
2450 }
2451 }
2452 pub fn stack_resize(&mut self, size: usize) -> Result<(), LuaError> {
2460 self.stack.resize_with(size, StackValue::default);
2461 Ok(())
2462 }
2463 pub fn stack_available(&mut self) -> usize {
2464 (self.stack_last.0 as usize).saturating_sub(self.top.0 as usize)
2465 }
2466 pub fn check_stack(&mut self, n: i32) -> Result<(), LuaError> {
2467 let free = (self.stack_last.0 as i32) - (self.top.0 as i32);
2468 if free <= n {
2469 self.grow_stack(n, true)?;
2470 }
2471 Ok(())
2472 }
2473 pub fn grow_stack(&mut self, n: i32, raise_error: bool) -> Result<(), LuaError> {
2482 crate::do_::grow_stack(self, n, raise_error).map(|_| ())
2483 }
2484
2485 #[inline(always)]
2486 pub fn get_ci(&self, idx: CallInfoIdx) -> &CallInfo { &self.call_info[idx.as_usize()] }
2487 #[inline(always)]
2488 pub fn get_ci_mut(&mut self, idx: CallInfoIdx) -> &mut CallInfo { &mut self.call_info[idx.as_usize()] }
2489 #[inline(always)]
2490 pub fn current_call_info(&self) -> &CallInfo { &self.call_info[self.ci.as_usize()] }
2491 #[inline(always)]
2492 pub fn current_call_info_mut(&mut self) -> &mut CallInfo { let i = self.ci.as_usize(); &mut self.call_info[i] }
2493 #[inline(always)]
2494 pub fn current_ci_idx(&self) -> CallInfoIdx { self.ci }
2495 pub fn call_stack_mut(&mut self) -> &mut Vec<CallInfo> { &mut self.call_info }
2496 #[inline(always)]
2497 pub fn next_ci(&mut self) -> Result<CallInfoIdx, LuaError> {
2498 match self.call_info[self.ci.as_usize()].next {
2499 Some(idx) => Ok(idx),
2500 None => Ok(extend_ci(self)),
2501 }
2502 }
2503 #[inline(always)]
2504 pub fn prev_ci(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2505 pub fn get_prev_ci(&self, idx: CallInfoIdx) -> Option<&CallInfo> {
2506 self.call_info[idx.as_usize()]
2507 .previous
2508 .map(|p| &self.call_info[p.as_usize()])
2509 }
2510 #[inline(always)]
2511 pub fn is_base_ci(&self, idx: CallInfoIdx) -> bool { idx.as_usize() == 0 }
2512 #[inline(always)]
2513 pub fn is_current_ci(&self, idx: CallInfoIdx) -> bool { idx == self.ci }
2514 pub fn ci_next_func(&self, idx: CallInfoIdx) -> StackIdx {
2515 let next = self.call_info[idx.as_usize()]
2516 .next
2517 .expect("ci_next_func: no next CallInfo");
2518 self.call_info[next.as_usize()].func
2519 }
2520 #[inline(always)]
2521 pub fn ci_top(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].top }
2522 #[inline(always)]
2523 pub fn ci_trap(&mut self, idx: CallInfoIdx) -> bool {
2524 if let CallInfoFrame::Lua { trap, .. } = self.call_info[idx.as_usize()].u {
2525 trap
2526 } else {
2527 false
2528 }
2529 }
2530 #[inline(always)]
2531 pub fn ci_savedpc(&self, idx: CallInfoIdx) -> u32 { self.call_info[idx.as_usize()].saved_pc() }
2532 #[inline(always)]
2533 pub fn set_ci_savedpc(&mut self, idx: CallInfoIdx, pc: u32) {
2534 self.call_info[idx.as_usize()].set_saved_pc(pc);
2535 }
2536 #[inline(always)]
2537 pub fn set_ci_previous(&mut self, idx: CallInfoIdx) {
2538 self.ci = self.call_info[idx.as_usize()]
2539 .previous
2540 .expect("set_ci_previous: returning frame has no previous CallInfo");
2541 }
2542 #[inline(always)]
2543 pub fn ci_previous(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2544 #[inline(always)]
2545 pub fn ci_adjust_func(&mut self, idx: CallInfoIdx, delta: i32) {
2546 let ci = &mut self.call_info[idx.as_usize()];
2547 ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
2548 }
2549 #[inline(always)]
2550 pub fn ci_base(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].func + 1 }
2551 #[inline(always)]
2552 pub fn ci_is_fresh(&self, idx: CallInfoIdx) -> bool {
2553 (self.call_info[idx.as_usize()].callstatus & CIST_FRESH) != 0
2554 }
2555 #[inline(always)]
2556 pub fn ci_lua_closure(&self, idx: CallInfoIdx) -> Option<GcRef<lua_types::closure::LuaLClosure>> {
2557 let func_idx = self.call_info[idx.as_usize()].func;
2558 match self.get_at(func_idx) {
2559 LuaValue::Function(lua_types::closure::LuaClosure::Lua(cl)) => Some(cl),
2560 _ => None,
2561 }
2562 }
2563 #[inline(always)]
2564 pub fn ci_nextraargs(&self, idx: CallInfoIdx) -> i32 {
2565 self.call_info[idx.as_usize()].nextra_args()
2566 }
2567 #[inline(always)]
2568 pub fn ci_nres(&self, idx: CallInfoIdx) -> i32 {
2569 self.call_info[idx.as_usize()].u2.value
2570 }
2571 #[inline(always)]
2572 pub fn ci_nres_set(&mut self, idx: CallInfoIdx, n: i32) {
2573 self.call_info[idx.as_usize()].u2.value = n;
2574 }
2575 #[inline(always)]
2576 pub fn ci_nresults(&self, idx: CallInfoIdx) -> i32 { self.call_info[idx.as_usize()].nresults as i32 }
2577 pub fn ci_prev_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2578 let pc = self.call_info[idx.as_usize()].saved_pc();
2579 let cl = self.ci_lua_closure(idx)
2580 .expect("ci_prev_instruction: CallInfo does not hold a Lua closure");
2581 cl.proto.code[(pc - 1) as usize]
2582 }
2583 pub fn ci_prev2_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2584 let pc = self.call_info[idx.as_usize()].saved_pc();
2585 let cl = self.ci_lua_closure(idx)
2586 .expect("ci_prev2_instruction: CallInfo does not hold a Lua closure");
2587 cl.proto.code[(pc - 2) as usize]
2588 }
2589 pub fn ci_skip_next_instruction(&mut self, idx: CallInfoIdx) {
2590 let pc = self.call_info[idx.as_usize()].saved_pc();
2591 self.call_info[idx.as_usize()].set_saved_pc(pc + 1);
2592 }
2593 pub fn ci_step_pc_back(&mut self, idx: CallInfoIdx) {
2594 let pc = self.call_info[idx.as_usize()].saved_pc();
2595 self.call_info[idx.as_usize()].set_saved_pc(pc - 1);
2596 }
2597 pub fn get_ci_pcrel(&mut self, idx: CallInfoIdx) -> u32 {
2598 self.call_info[idx.as_usize()].saved_pc().saturating_sub(1)
2599 }
2600 pub fn get_ci_u2_funcidx(&mut self, idx: CallInfoIdx) -> i32 {
2601 self.call_info[idx.as_usize()].u2.value
2602 }
2603 pub fn get_ci_u2_nres(&mut self, idx: CallInfoIdx) -> i32 {
2604 self.call_info[idx.as_usize()].u2.value
2605 }
2606 pub fn get_ci_u2_nyield(&mut self, idx: CallInfoIdx) -> i32 {
2607 self.call_info[idx.as_usize()].u2.value
2608 }
2609 pub fn get_ci_vararg_info(&mut self, idx: CallInfoIdx) -> (bool, i32, i32) {
2610 let nextraargs = self.call_info[idx.as_usize()].nextra_args();
2611 match self.ci_lua_closure(idx) {
2612 Some(cl) => (cl.proto.is_vararg, nextraargs, cl.proto.numparams as i32),
2613 None => (false, nextraargs, 0),
2614 }
2615 }
2616 pub fn get_ci_lua_proto_numparams(&mut self, idx: CallInfoIdx) -> u8 {
2617 self.ci_lua_closure(idx)
2618 .map(|cl| cl.proto.numparams)
2619 .unwrap_or(0)
2620 }
2621 pub fn set_ci_u2_nres(&mut self, idx: CallInfoIdx, n: i32) {
2622 self.call_info[idx.as_usize()].u2.value = n;
2623 }
2624 pub fn set_ci_u2_nyield(&mut self, idx: CallInfoIdx, n: i32) {
2625 self.call_info[idx.as_usize()].u2.value = n;
2626 }
2627 pub fn set_ci_transfer_info(&mut self, idx: CallInfoIdx, ftransfer: u16, ntransfer: u16) {
2628 let ci = &mut self.call_info[idx.as_usize()];
2629 ci.u2.ftransfer = ftransfer;
2630 ci.u2.ntransfer = ntransfer;
2631 }
2632 pub fn shrink_ci(&mut self) { shrink_ci(self) }
2633 pub fn check_c_stack(&mut self) -> Result<(), LuaError> { check_c_stack(self) }
2634
2635 pub fn status(&mut self) -> LuaStatus { LuaStatus::from_raw(self.status as i32) }
2636 pub fn errfunc(&mut self) -> isize { self.errfunc }
2637 pub fn old_pc(&mut self) -> u32 { self.oldpc }
2638 pub fn set_old_pc(&mut self, pc: u32) { self.oldpc = pc; }
2639 pub fn set_oldpc(&mut self, pc: u32) { self.oldpc = pc; }
2640 pub fn _hook_call_noargs(&mut self) {}
2641 pub fn hook(&self) -> Option<&Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>> {
2642 self.hook.as_ref()
2643 }
2644 pub fn has_hook(&mut self) -> bool { self.hook.is_some() }
2645 pub fn hook_count(&mut self) -> i32 { self.hookcount }
2646 pub fn set_hook_count(&mut self, n: i32) { self.hookcount = n; }
2647 pub fn hook_mask(&self) -> u8 { self.hookmask }
2648 pub fn set_hook_mask(&mut self, m: u8) { self.hookmask = m; }
2649 pub fn base_hook_count(&self) -> i32 { self.basehookcount }
2650 pub fn set_base_hook_count(&mut self, n: i32) { self.basehookcount = n; }
2651 pub fn set_hook(&mut self, h: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>) {
2652 self.hook = h;
2653 }
2654 pub fn call_hook_event(&mut self, event: i32, line: i32) -> Result<(), LuaError> {
2655 crate::do_::hook(self, event, line, 0, 0)
2656 }
2657
2658 pub fn registry_value(&self) -> LuaValue { self.global().l_registry.clone() }
2659 pub fn registry_get(&self, key: usize) -> LuaValue {
2660 let reg = self.global().l_registry.clone();
2661 match reg {
2662 LuaValue::Table(t) => t.get(&LuaValue::Int(key as i64)),
2663 _ => LuaValue::Nil,
2664 }
2665 }
2666
2667 pub fn new_string(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> { self.intern_or_create_str(bytes) }
2668
2669 pub fn new_proto(&mut self) -> GcRef<LuaProto> {
2681 self.mark_gc_check_needed();
2682 GcRef::new(LuaProto::placeholder())
2683 }
2684
2685 pub fn new_lclosure(&mut self, proto: GcRef<LuaProto>, nupvals: usize) -> GcRef<LuaClosureLua> {
2687 self.mark_gc_check_needed();
2688 let mut upvals = Vec::with_capacity(nupvals);
2689 for _ in 0..nupvals {
2690 upvals.push(std::cell::Cell::new(self.new_upval_closed(LuaValue::Nil)));
2691 }
2692 let closure = GcRef::new(LuaClosureLua { proto, upvals });
2693 closure.account_buffer(closure.buffer_bytes() as isize);
2694 closure
2695 }
2696
2697 pub fn new_upval_closed(&mut self, v: LuaValue) -> GcRef<UpVal> {
2699 self.mark_gc_check_needed();
2700 GcRef::new(UpVal::closed(v))
2701 }
2702
2703 pub fn new_upval_open(&mut self, thread_id: usize, level: StackIdx) -> GcRef<UpVal> {
2705 self.mark_gc_check_needed();
2706 GcRef::new(UpVal::open(thread_id, level))
2707 }
2708 pub fn intern_or_create_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2715 self.intern_str(bytes)
2716 }
2717 pub fn new_userdata(&mut self, _size: usize, _nuvalue: usize) -> Result<GcRef<LuaUserData>, LuaError> {
2718 Err(LuaError::runtime(format_args!("new_userdata not implemented in this Phase-B build; use new_userdata_typed instead")))
2719 }
2720 pub fn new_c_closure(&mut self, _f: LuaCFunction, _n: i32) -> Result<LuaClosure, LuaError> {
2721 Err(LuaError::runtime(format_args!("new_c_closure not implemented in this Phase-B build; use push_cclosure in lua_vm::api instead")))
2722 }
2723 pub fn push_closure(
2724 &mut self,
2725 proto_idx: usize,
2726 ci: CallInfoIdx,
2727 base: StackIdx,
2728 ra: StackIdx,
2729 ) -> Result<(), LuaError> {
2730 let parent_cl = self.ci_lua_closure(ci).expect(
2731 "push_closure: current frame is not a Lua closure",
2732 );
2733 let child_proto = parent_cl.proto.p[proto_idx].clone();
2734 let nup = child_proto.upvalues.len();
2735 let mut upvals: Vec<std::cell::Cell<GcRef<UpVal>>> = Vec::with_capacity(nup);
2736 for i in 0..nup {
2737 let desc = &child_proto.upvalues[i];
2738 let uv = if desc.instack {
2739 let level = base + desc.idx as i32;
2740 crate::func::find_upval(self, level)
2741 } else {
2742 parent_cl.upval(desc.idx as usize)
2743 };
2744 upvals.push(std::cell::Cell::new(uv));
2745 }
2746 let cache_enabled = matches!(
2750 self.global().lua_version,
2751 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
2752 );
2753 if cache_enabled {
2754 if let Some(cached) = child_proto.cache.borrow().as_ref() {
2755 if cached.upvals.len() == nup
2756 && (0..nup).all(|i| GcRef::ptr_eq(&cached.upvals[i].get(), &upvals[i].get()))
2757 {
2758 let reused = cached.clone();
2759 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(reused)));
2760 return Ok(());
2761 }
2762 }
2763 }
2764 self.mark_gc_check_needed();
2767 let new_cl = GcRef::new(LuaClosureLua {
2768 proto: child_proto.clone(),
2769 upvals,
2770 });
2771 new_cl.account_buffer(new_cl.buffer_bytes() as isize);
2772 if cache_enabled {
2773 *child_proto.cache.borrow_mut() = Some(new_cl.clone());
2774 }
2775 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(new_cl)));
2776 Ok(())
2777 }
2778 pub fn new_tbc_upval(&mut self, idx: StackIdx) -> Result<(), LuaError> {
2779 crate::func::new_tbc_upval(self, idx)
2780 }
2781
2782 #[inline(always)]
2803 pub fn upvalue_get(&self, cl: &GcRef<LuaClosureLua>, n: usize) -> LuaValue {
2804 let uv = cl.upval(n);
2805 let (thread_id, idx) = match uv.try_open_payload() {
2806 Some(p) => p,
2807 None => return *uv.closed_value(),
2808 };
2809 let current = self.cached_thread_id;
2810 let tid = thread_id as u64;
2811 if tid == current {
2812 return self.stack[idx.0 as usize].val;
2813 }
2814 self.upvalue_get_cross_thread(tid, idx)
2815 }
2816
2817 #[cold]
2818 #[inline(never)]
2819 fn upvalue_get_cross_thread(&self, tid: u64, idx: StackIdx) -> LuaValue {
2820 let entry_rc = {
2821 let g = self.global();
2822 g.threads.get(&tid).map(|e| e.state.clone())
2823 };
2824 if let Some(rc) = entry_rc {
2825 if let Ok(home_state) = rc.try_borrow() {
2826 return home_state.get_at(idx);
2827 }
2828 }
2829 let g = self.global();
2830 match g.cross_thread_upvals.get(&(tid, idx)) {
2831 Some(v) => *v,
2832 None => LuaValue::Nil,
2833 }
2834 }
2835 #[inline(always)]
2844 pub fn upvalue_set(&mut self, cl: &GcRef<LuaClosureLua>, n: usize, val: LuaValue) -> Result<(), LuaError> {
2845 let uv = cl.upval(n);
2846 match uv.try_open_payload() {
2847 Some((thread_id, idx)) => {
2848 let tid = thread_id as u64;
2849 let current = self.cached_thread_id;
2850 if tid == current {
2851 self.stack[idx.0 as usize].val = val;
2852 } else {
2853 self.upvalue_set_cross_thread(tid, idx, val)?;
2854 }
2855 }
2856 None => {
2857 uv.set_closed_value(val);
2858 }
2859 }
2860 self.gc_barrier_upval(&uv, &val);
2861 Ok(())
2862 }
2863
2864 #[cold]
2865 #[inline(never)]
2866 fn upvalue_set_cross_thread(
2867 &mut self,
2868 tid: u64,
2869 idx: StackIdx,
2870 val: LuaValue,
2871 ) -> Result<(), LuaError> {
2872 let entry_rc = {
2873 let g = self.global();
2874 g.threads.get(&tid).map(|e| e.state.clone())
2875 };
2876 if let Some(rc) = entry_rc {
2877 if let Ok(mut home_state) = rc.try_borrow_mut() {
2878 home_state.set_at(idx, val);
2879 return Ok(());
2880 }
2881 }
2882 let mut g = self.global_mut();
2883 g.cross_thread_upvals.insert((tid, idx), val);
2884 Ok(())
2885 }
2886
2887 pub fn protected_call_raw(&mut self, func: StackIdx, nresults: i32, errfunc: StackIdx) -> Result<(), LuaError> {
2888 let ef = errfunc.0 as isize;
2889 let status = crate::do_::pcall(
2890 self,
2891 |s| s.call_no_yield(func, nresults),
2892 func,
2893 ef,
2894 );
2895 match status {
2896 LuaStatus::Ok => Ok(()),
2897 LuaStatus::ErrSyntax => {
2898 let err_val = self.get_at(func);
2899 self.set_top(func);
2900 Err(LuaError::Syntax(err_val))
2901 }
2902 LuaStatus::Yield => {
2903 self.set_top(func);
2904 Err(LuaError::Yield)
2905 }
2906 _ => {
2907 let err_val = self.get_at(func);
2908 self.set_top(func);
2909 Err(LuaError::Runtime(err_val))
2910 }
2911 }
2912 }
2913 pub fn protected_parser(&mut self, z: crate::zio::ZIO, name: &[u8], mode: Option<&[u8]>) -> LuaStatus {
2914 crate::do_::protected_parser(self, z, name, mode)
2915 }
2916 pub fn do_call(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2917 crate::do_::call(self, func, nresults)
2918 }
2919 pub fn do_call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2920 crate::do_::callnoyield(self, func, nresults)
2921 }
2922 pub fn call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2923 crate::do_::callnoyield(self, func, nresults)
2924 }
2925 pub fn call_at(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2926 crate::do_::call(self, func, nresults)
2927 }
2928 #[inline(always)]
2929 pub fn precall(&mut self, func: StackIdx, nresults: i32) -> Result<Option<CallInfoIdx>, LuaError> {
2930 crate::do_::precall(self, func, nresults)
2931 }
2932 #[inline(always)]
2933 pub fn pretailcall(
2934 &mut self,
2935 ci: CallInfoIdx,
2936 func: StackIdx,
2937 narg1: i32,
2938 delta: i32,
2939 ) -> Result<i32, LuaError> {
2940 crate::do_::pretailcall(self, ci, func, narg1, delta)
2941 }
2942 #[inline(always)]
2943 pub fn poscall<N: TryInto<i32>>(&mut self, ci: CallInfoIdx, nres: N) -> Result<(), LuaError>
2944 where
2945 <N as TryInto<i32>>::Error: std::fmt::Debug,
2946 {
2947 let n = nres.try_into().expect("poscall: nres out of i32 range");
2948 crate::do_::poscall(self, ci, n)
2949 }
2950 pub fn adjust_results(&mut self, nresults: i32) {
2951 const LUA_MULTRET: i32 = -1;
2952 if nresults <= LUA_MULTRET {
2953 let ci_idx = self.ci.as_usize();
2954 if self.call_info[ci_idx].top.0 < self.top.0 {
2955 self.call_info[ci_idx].top = self.top;
2956 }
2957 }
2958 }
2959 pub fn adjust_varargs(
2960 &mut self,
2961 ci: CallInfoIdx,
2962 nfixparams: i32,
2963 cl: &GcRef<lua_types::closure::LuaLClosure>,
2964 ) -> Result<(), LuaError> {
2965 crate::tagmethods::adjust_varargs(self, nfixparams, ci, &cl.0.proto)
2966 }
2967 pub fn get_varargs(
2968 &mut self,
2969 ci: CallInfoIdx,
2970 ra: StackIdx,
2971 n: i32,
2972 ) -> Result<i32, LuaError> {
2973 crate::tagmethods::get_varargs(self, ci, ra, n)?;
2974 Ok(0)
2975 }
2976
2977 pub fn close_upvals(&mut self, level: StackIdx) -> Result<(), LuaError> {
2978 crate::func::close_upval(self, level);
2979 Ok(())
2980 }
2981 pub fn close_upvals_status(&mut self, level: StackIdx, _status: i32) -> Result<(), LuaError> {
2982 crate::func::close_upval(self, level);
2983 Ok(())
2984 }
2985 pub fn close_upvals_from_base(&mut self, ci: CallInfoIdx) -> Result<(), LuaError> {
2986 let base = self.ci_base(ci);
2987 crate::func::close_upval(self, base);
2988 Ok(())
2989 }
2990
2991 pub fn arith_op(&mut self, op: i32, p1: &LuaValue, p2: &LuaValue) -> Result<LuaValue, LuaError> {
2992 let arith_op = match op {
2993 0 => lua_types::arith::ArithOp::Add,
2994 1 => lua_types::arith::ArithOp::Sub,
2995 2 => lua_types::arith::ArithOp::Mul,
2996 3 => lua_types::arith::ArithOp::Mod,
2997 4 => lua_types::arith::ArithOp::Pow,
2998 5 => lua_types::arith::ArithOp::Div,
2999 6 => lua_types::arith::ArithOp::Idiv,
3000 7 => lua_types::arith::ArithOp::Band,
3001 8 => lua_types::arith::ArithOp::Bor,
3002 9 => lua_types::arith::ArithOp::Bxor,
3003 10 => lua_types::arith::ArithOp::Shl,
3004 11 => lua_types::arith::ArithOp::Shr,
3005 12 => lua_types::arith::ArithOp::Unm,
3006 13 => lua_types::arith::ArithOp::Bnot,
3007 _ => return Err(LuaError::runtime(format_args!("invalid arith op {}", op))),
3008 };
3009 let mut res = LuaValue::Nil;
3010 if crate::object::raw_arith(self, arith_op, p1, p2, &mut res)? {
3011 Ok(res)
3012 } else {
3013 Err(LuaError::arith_error(p1, p2, "perform arithmetic on"))
3014 }
3015 }
3016 pub fn concat(&mut self, n: i32) -> Result<(), LuaError> {
3017 crate::vm::concat(self, n)
3018 }
3019 pub fn less_than(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3020 crate::vm::less_than(self, l, r)
3021 }
3022 pub fn less_equal(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3023 crate::vm::less_equal(self, l, r)
3024 }
3025 pub fn equal_obj(&self, _ctx: Option<&LuaValue>, l: &LuaValue, r: &LuaValue) -> bool {
3026 crate::vm::equal_obj(None, l, r).unwrap_or(false)
3027 }
3028 pub fn equal_obj_with_tm(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3029 crate::vm::equal_obj(Some(self), l, r)
3030 }
3031 pub fn obj_len(&mut self, v: &LuaValue) -> Result<LuaValue, LuaError> {
3032 match v {
3033 LuaValue::Table(_) => {
3034 let consult_len_tm =
3037 !matches!(self.global().lua_version, lua_types::LuaVersion::V51);
3038 let tm = if consult_len_tm {
3039 let mt = self.table_metatable(v);
3040 self.fast_tm_table(mt.as_ref(), TagMethod::Len)
3041 } else {
3042 LuaValue::Nil
3043 };
3044 if matches!(tm, LuaValue::Nil) {
3045 let n = self.table_length(v)?;
3046 return Ok(LuaValue::Int(n));
3047 }
3048 self.push(LuaValue::Nil);
3049 let slot = StackIdx(self.top.0 - 1);
3050 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3051 Ok(self.pop())
3052 }
3053 LuaValue::Str(s) => Ok(LuaValue::Int(s.len() as i64)),
3054 other => {
3055 let tm = crate::tagmethods::get_tm_by_obj(self, other, crate::tagmethods::TagMethod::Len);
3056 if matches!(tm, LuaValue::Nil) {
3057 let mut msg = b"attempt to get length of a ".to_vec();
3058 msg.extend_from_slice(&self.obj_type_name(other));
3059 msg.extend_from_slice(b" value");
3060 return Err(crate::debug::prefixed_runtime_pub(self, msg));
3061 }
3062 self.push(LuaValue::Nil);
3063 let slot = StackIdx(self.top.0 - 1);
3064 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3065 Ok(self.pop())
3066 }
3067 }
3068 }
3069 pub fn obj_to_string(&mut self, idx: i32) -> Result<GcRef<LuaString>, LuaError> {
3070 let slot: StackIdx = if idx > 0 {
3071 let ci_func = self.current_call_info().func;
3072 ci_func + idx
3073 } else {
3074 debug_assert!(idx != 0, "invalid index");
3075 StackIdx((self.top_idx().0 as i32 + idx) as u32)
3076 };
3077 let val = self.get_at(slot);
3078 match val {
3079 LuaValue::Str(s) => Ok(s),
3080 LuaValue::Int(_) | LuaValue::Float(_) => {
3081 let s = crate::object::num_to_string(self, &val)?;
3082 self.set_at(slot, LuaValue::Str(s.clone()));
3083 Ok(s)
3084 }
3085 _ => Err(LuaError::type_error(&val, "convert to string")),
3086 }
3087 }
3088 pub fn coerce_to_string(&mut self, idx: StackIdx) -> Result<GcRef<LuaString>, LuaError> {
3089 let val = self.get_at(idx);
3090 match val {
3091 LuaValue::Str(s) => Ok(s),
3092 LuaValue::Int(_) | LuaValue::Float(_) => {
3093 let s = crate::object::num_to_string(self, &val)?;
3094 self.set_at(idx, LuaValue::Str(s.clone()));
3095 Ok(s)
3096 }
3097 _ => Err(LuaError::type_error(&val, "convert to string")),
3098 }
3099 }
3100 pub fn str_to_num(&mut self, s: &[u8]) -> Option<(LuaValue, usize)> {
3101 let mut out = LuaValue::Nil;
3102 let sz = crate::object::str2num(s, &mut out);
3103 if sz == 0 { None } else { Some((out, sz)) }
3104 }
3105
3106 pub fn fast_get(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3107 let LuaValue::Table(tbl) = t else { return Ok(None); };
3108 let v = tbl.get(k);
3109 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3110 }
3111 pub fn fast_get_int(&mut self, t: &LuaValue, k: i64) -> Result<Option<LuaValue>, LuaError> {
3112 let LuaValue::Table(tbl) = t else { return Ok(None); };
3113 let v = tbl.get_int(k);
3114 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3115 }
3116 pub fn fast_get_short_str(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3117 let LuaValue::Table(tbl) = t else { return Ok(None); };
3118 let LuaValue::Str(s) = k else { return Ok(None); };
3119 let v = tbl.get_short_str(s);
3120 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3121 }
3122 pub fn fast_tm_table(&mut self, t: Option<&GcRef<LuaTable>>, tm: TagMethod) -> LuaValue {
3123 let Some(mt) = t else { return LuaValue::Nil; };
3124 debug_assert!((tm as u8) <= TagMethod::Eq as u8);
3125 let ename = self.global().tmname[tm as usize].clone();
3126 mt.get_short_str(&ename)
3127 }
3128 pub fn fast_tm_ud(&mut self, u: &GcRef<LuaUserData>, tm: TagMethod) -> LuaValue {
3129 let mt = u.metatable();
3131 self.fast_tm_table(mt.as_ref(), tm)
3132 }
3133
3134 pub fn table_get_with_tm(&mut self, t: &LuaValue, k: &LuaValue) -> Result<LuaValue, LuaError> {
3135 if let LuaValue::Table(tbl) = t {
3141 if tbl.metatable().is_none() {
3142 return Ok(tbl.get(k));
3143 }
3144 }
3145 if let Some(v) = self.fast_get(t, k)? {
3146 return Ok(v);
3147 }
3148 let res = self.top_idx();
3149 self.push(LuaValue::Nil);
3150 crate::vm::finish_get(self, t.clone(), k.clone(), res, true, None)?;
3151 let value = self.get_at(res);
3152 self.pop();
3153 Ok(value)
3154 }
3155 #[inline]
3170 pub fn table_set_with_tm(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3171 if let LuaValue::Table(tbl) = t {
3172 if tbl.metatable().is_none() {
3173 self.gc_barrier_back(t, &v);
3174 return self.table_raw_set(t, k, v);
3175 }
3176 }
3177 if self.fast_get(t, &k)?.is_some() {
3178 self.gc_barrier_back(t, &v);
3179 return self.table_raw_set(t, k, v);
3180 }
3181 crate::vm::finish_set(self, t.clone(), k, v, true, None, None)
3182 }
3183 #[inline]
3184 pub fn table_raw_set(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3185 let LuaValue::Table(tbl) = t else {
3186 return Err(LuaError::type_error(t, "index"));
3187 };
3188 let tbl = tbl.clone();
3189 tbl.raw_set(self, k, v)
3190 }
3191 #[inline]
3192 pub fn table_array_set(&mut self, t: &LuaValue, idx: usize, v: LuaValue) -> Result<(), LuaError> {
3193 let LuaValue::Table(tbl) = t else {
3194 return Err(LuaError::type_error(t, "index"));
3195 };
3196 let tbl = tbl.clone();
3197 tbl.raw_set_int(self, idx as i64 + 1, v)
3198 }
3199 pub fn table_ensure_array(&mut self, t: &LuaValue, n: usize) -> Result<(), LuaError> {
3200 let LuaValue::Table(tbl) = t else {
3201 return Err(LuaError::type_error(t, "index"));
3202 };
3203 if n > tbl.array_len() {
3204 tbl.resize(self, n, 0)?;
3205 }
3206 Ok(())
3207 }
3208 pub fn table_length(&mut self, t: &LuaValue) -> Result<i64, LuaError> {
3209 let LuaValue::Table(tbl) = t else {
3210 return Err(LuaError::type_error(t, "get length of"));
3211 };
3212 Ok(tbl.getn() as i64)
3213 }
3214 pub fn table_metatable(&mut self, v: &LuaValue) -> Option<GcRef<LuaTable>> {
3215 match v {
3216 LuaValue::Table(t) => t.metatable(),
3217 LuaValue::UserData(u) => u.metatable(),
3218 other => {
3219 let idx = other.base_type() as usize;
3220 self.global().mt[idx].clone()
3221 }
3222 }
3223 }
3224 pub fn table_resize(&mut self, t: &GcRef<LuaTable>, na: usize, nh: usize) -> Result<(), LuaError> {
3225 self.mark_gc_check_needed();
3226 t.resize(self, na, nh)
3227 }
3228 pub fn table_getn(&self, t: &GcRef<LuaTable>) -> i64 {
3229 let mut i: i64 = 1;
3237 loop {
3238 let v = t.get_int(i);
3239 if matches!(v, LuaValue::Nil) {
3240 return i - 1;
3241 }
3242 i += 1;
3243 }
3244 }
3245
3246 pub fn try_bin_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, p2: &LuaValue, p2_idx: Option<StackIdx>, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3247 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3248 crate::tagmethods::try_bin_tm(self, p1, p1_idx, p2, p2_idx, res, event)
3249 }
3250 pub fn try_bin_i_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, imm: i64, flip: bool, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3251 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3252 crate::tagmethods::try_bini_tm(self, p1, p1_idx, imm, flip, res, event)
3253 }
3254 pub fn try_bin_assoc_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, p2: &LuaValue, p2_idx: Option<StackIdx>, flip: bool, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3255 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3256 crate::tagmethods::try_bin_assoc_tm(self, p1, p1_idx, p2, p2_idx, flip, res, event)
3257 }
3258 pub fn try_concat_tm(&mut self, _p1: &LuaValue, _p2: &LuaValue) -> Result<(), LuaError> {
3259 crate::tagmethods::try_concat_tm(self)
3260 }
3261 pub fn call_tm(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, p3: &LuaValue) -> Result<(), LuaError> {
3262 crate::tagmethods::call_tm(self, f, p1.clone(), p2.clone(), p3.clone())
3263 }
3264 pub fn call_tm_res(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, res: StackIdx) -> Result<(), LuaError> {
3265 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)
3266 }
3267 pub fn call_tm_res_bool(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue) -> Result<bool, LuaError> {
3268 let res = self.top_idx();
3269 self.push(LuaValue::Nil);
3270 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)?;
3271 let result = self.get_at(res).clone();
3272 self.pop();
3273 Ok(!matches!(result, LuaValue::Nil | LuaValue::Bool(false)))
3274 }
3275 pub fn call_order_tm(&mut self, p1: &LuaValue, p2: &LuaValue, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3276 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3277 crate::tagmethods::call_order_tm(self, p1, p2, event)
3278 }
3279 pub fn call_order_i_tm(&mut self, p1: &LuaValue, v2: i64, flip: bool, isfloat: bool, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3280 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3281 crate::tagmethods::call_orderi_tm(self, p1, v2 as i32, flip, isfloat, event)
3282 }
3283
3284 #[inline(always)]
3285 pub fn proto_code(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, pc: u32) -> lua_types::opcode::Instruction {
3286 cl.proto.code[pc as usize]
3287 }
3288 #[inline(always)]
3289 pub fn proto_const(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> LuaValue {
3290 cl.proto.k[idx].clone()
3291 }
3292 #[inline(always)]
3298 pub fn proto_const_int(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<i64> {
3299 match &cl.proto.k[idx] {
3300 LuaValue::Int(v) => Some(*v),
3301 _ => None,
3302 }
3303 }
3304 #[inline(always)]
3308 pub fn proto_const_num(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<f64> {
3309 match &cl.proto.k[idx] {
3310 LuaValue::Float(f) => Some(*f),
3311 LuaValue::Int(v) => Some(*v as f64),
3312 _ => None,
3313 }
3314 }
3315 pub fn get_proto_instr(&self, ci: CallInfoIdx, pc: u32) -> lua_types::opcode::Instruction {
3316 let cl = self.ci_lua_closure(ci)
3317 .expect("get_proto_instr: CallInfo does not hold a Lua closure");
3318 cl.proto.code[pc as usize]
3319 }
3320 pub fn trace_call(&mut self, _idx: CallInfoIdx) -> Result<bool, LuaError> {
3325 Ok(crate::debug::trace_call(self)? != 0)
3326 }
3327 pub fn trace_exec(&mut self, _idx: CallInfoIdx, pc: u32) -> Result<bool, LuaError> {
3330 Ok(crate::debug::trace_exec(self, pc)? != 0)
3331 }
3332 pub fn hook_call(&mut self, idx: CallInfoIdx) -> Result<(), LuaError> {
3333 crate::do_::hookcall(self, idx)
3334 }
3335 #[inline(always)]
3336 fn gc_step_flags(&self) -> Option<(bool, bool)> {
3337 let g = self.global();
3338 if !g.is_gc_running() {
3339 return None;
3340 }
3341 let should_collect = g.heap.would_collect();
3342 let has_finalizers = g.finalizers.has_to_be_finalized();
3343 if should_collect || has_finalizers {
3344 Some((should_collect, has_finalizers))
3345 } else {
3346 None
3347 }
3348 }
3349
3350 #[inline(always)]
3351 fn should_check_gc(&mut self) -> bool {
3352 if self.gc_check_needed {
3353 return true;
3354 }
3355 if self.global().finalizers.has_to_be_finalized() {
3356 self.gc_check_needed = true;
3357 return true;
3358 }
3359 false
3360 }
3361
3362 #[inline(always)]
3363 pub(crate) fn mark_gc_check_needed(&mut self) {
3364 self.gc_check_needed = true;
3365 }
3366
3367 #[inline(always)]
3368 pub fn gc_check_step(&mut self) {
3369 if !self.allowhook {
3370 return;
3371 }
3372 if !self.should_check_gc() {
3373 return;
3374 }
3375 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3376 self.gc_check_needed = false;
3377 return;
3378 };
3379 if should_collect || has_finalizers {
3380 if should_collect {
3381 self.gc().check_step();
3382 }
3383 crate::api::run_pending_finalizers(self);
3384 self.gc_check_needed = true;
3385 }
3386 let should_keep_checking = {
3387 let g = self.global();
3388 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3389 };
3390 self.gc_check_needed = should_keep_checking;
3391 }
3392 #[inline(always)]
3393 pub fn gc_cond_step(&mut self) {
3394 if !self.allowhook {
3395 return;
3396 }
3397 if !self.should_check_gc() {
3398 return;
3399 }
3400 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3401 self.gc_check_needed = false;
3402 return;
3403 };
3404 if should_collect || has_finalizers {
3405 if should_collect {
3406 self.gc().check_step();
3407 }
3408 crate::api::run_pending_finalizers(self);
3409 self.gc_check_needed = true;
3410 }
3411 let should_keep_checking = {
3412 let g = self.global();
3413 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3414 };
3415 self.gc_check_needed = should_keep_checking;
3416 }
3417 pub fn gc_barrier_back(&mut self, t: &dyn std::any::Any, v: &LuaValue) {
3418 self.gc().barrier_back(t, v);
3419 }
3420 pub fn gc_barrier_upval(&mut self, uv: &GcRef<UpVal>, v: &LuaValue) {
3421 self.gc().barrier(uv, v);
3422 }
3423 pub fn is_main_thread(&mut self) -> bool {
3429 let g = self.global();
3430 g.current_thread_id == g.main_thread_id
3431 }
3432 pub fn obj_type_name<'v>(&self, v: &'v LuaValue) -> std::borrow::Cow<'static, [u8]> {
3433 match v {
3434 LuaValue::LightUserData(_) => std::borrow::Cow::Borrowed(b"light userdata"),
3435 LuaValue::Table(t) => {
3436 if let Some(mt) = t.metatable() {
3437 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3438 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3439 }
3440 }
3441 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3442 }
3443 LuaValue::UserData(u) => {
3444 if let Some(mt) = u.metatable() {
3445 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3446 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3447 }
3448 }
3449 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3450 }
3451 _ => std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type())),
3452 }
3453 }
3454
3455 pub fn full_type_name(&mut self, v: &LuaValue) -> Result<Vec<u8>, LuaError> {
3456 crate::tagmethods::obj_type_name(self, v)
3457 }
3458 pub fn emit_warning(&mut self, _msg: &[u8], _to_cont: bool) { warning(self, _msg, _to_cont) }
3459}
3460
3461pub struct GcHandle<'a> {
3467 _state: &'a mut LuaState,
3468}
3469
3470struct CollectRoots<'a> {
3477 global: &'a GlobalState,
3478 thread: &'a LuaState,
3479}
3480
3481#[derive(Clone, Copy)]
3482enum HeapCollectMode {
3483 Full,
3484 Step,
3485 Minor,
3486}
3487
3488impl<'a> lua_gc::Trace for CollectRoots<'a> {
3489 fn trace(&self, m: &mut lua_gc::Marker) {
3490 self.global.trace(m);
3491 self.thread.trace(m);
3492 }
3493}
3494
3495#[derive(Clone, Copy)]
3496enum BarrierKind {
3497 Forward,
3498 Backward,
3499}
3500
3501fn barrier_lua_value<P>(
3502 heap: &lua_gc::Heap,
3503 parent: GcRef<P>,
3504 child: &LuaValue,
3505 generational: bool,
3506 kind: BarrierKind,
3507)
3508where
3509 P: lua_gc::Trace + 'static,
3510{
3511 if generational && matches!(kind, BarrierKind::Backward) {
3512 heap.generational_backward_barrier(parent.0);
3513 }
3514 match child {
3515 LuaValue::Str(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3516 LuaValue::Table(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3517 LuaValue::Function(LuaClosure::Lua(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3518 LuaValue::Function(LuaClosure::C(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3519 LuaValue::UserData(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3520 LuaValue::Thread(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3521 LuaValue::Nil
3522 | LuaValue::Bool(_)
3523 | LuaValue::Int(_)
3524 | LuaValue::Float(_)
3525 | LuaValue::LightUserData(_)
3526 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3527 }
3528}
3529
3530fn barrier_gc_child<P, C>(
3531 heap: &lua_gc::Heap,
3532 parent: GcRef<P>,
3533 child: GcRef<C>,
3534 generational: bool,
3535 kind: BarrierKind,
3536)
3537where
3538 P: lua_gc::Trace + 'static,
3539 C: lua_gc::Trace + 'static,
3540{
3541 if generational && matches!(kind, BarrierKind::Forward) {
3542 heap.generational_forward_barrier(parent.0, child.0);
3543 } else if matches!(kind, BarrierKind::Backward) {
3544 heap.barrier_back(parent.0, child.0);
3545 } else {
3546 heap.barrier(parent.0, child.0);
3547 }
3548}
3549
3550fn barrier_child_any<P>(
3551 heap: &lua_gc::Heap,
3552 parent: GcRef<P>,
3553 child: &dyn std::any::Any,
3554 generational: bool,
3555 kind: BarrierKind,
3556)
3557where
3558 P: lua_gc::Trace + 'static,
3559{
3560 if let Some(v) = child.downcast_ref::<LuaValue>() {
3561 barrier_lua_value(heap, parent, v, generational, kind);
3562 } else if let Some(c) = child.downcast_ref::<GcRef<LuaString>>() {
3563 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3564 } else if let Some(c) = child.downcast_ref::<GcRef<LuaTable>>() {
3565 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3566 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureLua>>() {
3567 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3568 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureC>>() {
3569 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3570 } else if let Some(c) = child.downcast_ref::<GcRef<LuaUserData>>() {
3571 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3572 } else if let Some(c) = child.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3573 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3574 } else if let Some(c) = child.downcast_ref::<GcRef<LuaProto>>() {
3575 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3576 } else if let Some(c) = child.downcast_ref::<GcRef<UpVal>>() {
3577 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3578 }
3579}
3580
3581fn barrier_any(
3582 heap: &lua_gc::Heap,
3583 parent: &dyn std::any::Any,
3584 child: &dyn std::any::Any,
3585 generational: bool,
3586 kind: BarrierKind,
3587) {
3588 if let Some(v) = parent.downcast_ref::<LuaValue>() {
3589 match v {
3590 LuaValue::Str(p) => barrier_child_any(heap, *p, child, generational, kind),
3591 LuaValue::Table(p) => barrier_child_any(heap, *p, child, generational, kind),
3592 LuaValue::Function(LuaClosure::Lua(p)) => barrier_child_any(heap, *p, child, generational, kind),
3593 LuaValue::Function(LuaClosure::C(p)) => barrier_child_any(heap, *p, child, generational, kind),
3594 LuaValue::UserData(p) => barrier_child_any(heap, *p, child, generational, kind),
3595 LuaValue::Thread(p) => barrier_child_any(heap, *p, child, generational, kind),
3596 LuaValue::Nil
3597 | LuaValue::Bool(_)
3598 | LuaValue::Int(_)
3599 | LuaValue::Float(_)
3600 | LuaValue::LightUserData(_)
3601 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3602 }
3603 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaString>>() {
3604 barrier_child_any(heap, p.clone(), child, generational, kind);
3605 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaTable>>() {
3606 barrier_child_any(heap, p.clone(), child, generational, kind);
3607 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureLua>>() {
3608 barrier_child_any(heap, p.clone(), child, generational, kind);
3609 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureC>>() {
3610 barrier_child_any(heap, p.clone(), child, generational, kind);
3611 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaUserData>>() {
3612 barrier_child_any(heap, p.clone(), child, generational, kind);
3613 } else if let Some(p) = parent.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3614 barrier_child_any(heap, p.clone(), child, generational, kind);
3615 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaProto>>() {
3616 barrier_child_any(heap, p.clone(), child, generational, kind);
3617 } else if let Some(p) = parent.downcast_ref::<GcRef<UpVal>>() {
3618 barrier_child_any(heap, p.clone(), child, generational, kind);
3619 }
3620}
3621
3622fn trace_reachable_threads(
3623 global: &GlobalState,
3624 _current_thread_id: u64,
3625 marker: &mut lua_gc::Marker,
3626) {
3627 use lua_gc::Trace;
3628
3629 loop {
3630 let visited_before = marker.visited_count();
3631 for (id, entry) in global.threads.iter() {
3632 if thread_entry_marked_alive(marker, *id, entry) {
3633 if let Ok(thread) = entry.state.try_borrow() {
3634 thread.trace(marker);
3635 }
3636 }
3637 }
3638 marker.drain_gray_queue();
3639 if marker.visited_count() == visited_before {
3640 break;
3641 }
3642 }
3643}
3644
3645fn thread_entry_marked_alive(
3646 marker: &lua_gc::Marker,
3647 id: u64,
3648 entry: &ThreadRegistryEntry,
3649) -> bool {
3650 marker.is_marked_or_old(entry.value.0) && entry.value.id == id
3651}
3652
3653fn lua_value_marked_or_old(marker: &lua_gc::Marker, value: &LuaValue) -> bool {
3654 match value {
3655 LuaValue::Str(v) => marker.is_marked_or_old(v.0),
3656 LuaValue::Table(v) => marker.is_marked_or_old(v.0),
3657 LuaValue::Function(LuaClosure::Lua(v)) => marker.is_marked_or_old(v.0),
3658 LuaValue::Function(LuaClosure::C(v)) => marker.is_marked_or_old(v.0),
3659 LuaValue::UserData(v) => marker.is_marked_or_old(v.0),
3660 LuaValue::Thread(v) => marker.is_marked_or_old(v.0),
3661 LuaValue::Nil
3662 | LuaValue::Bool(_)
3663 | LuaValue::Int(_)
3664 | LuaValue::Float(_)
3665 | LuaValue::LightUserData(_)
3666 | LuaValue::Function(LuaClosure::LightC(_)) => true,
3667 }
3668}
3669
3670fn lua_value_identity(value: &LuaValue) -> Option<usize> {
3671 match value {
3672 LuaValue::Str(v) => Some(v.identity()),
3673 LuaValue::Table(v) => Some(v.identity()),
3674 LuaValue::Function(LuaClosure::Lua(v)) => Some(v.identity()),
3675 LuaValue::Function(LuaClosure::C(v)) => Some(v.identity()),
3676 LuaValue::UserData(v) => Some(v.identity()),
3677 LuaValue::Thread(v) => Some(v.identity()),
3678 LuaValue::Nil
3679 | LuaValue::Bool(_)
3680 | LuaValue::Int(_)
3681 | LuaValue::Float(_)
3682 | LuaValue::LightUserData(_)
3683 | LuaValue::Function(LuaClosure::LightC(_)) => None,
3684 }
3685}
3686
3687fn finalizer_marked_or_old(marker: &lua_gc::Marker, object: &FinalizerObject) -> bool {
3688 match object {
3689 FinalizerObject::Table(t) => marker.is_marked_or_old(t.0),
3690 FinalizerObject::UserData(u) => marker.is_marked_or_old(u.0),
3691 }
3692}
3693
3694fn weak_snapshot_tables<'a>(
3695 snapshot: &'a lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>>,
3696) -> impl Iterator<Item = &'a GcRef<LuaTable>> {
3697 snapshot
3698 .weak_values
3699 .iter()
3700 .chain(snapshot.ephemeron.iter())
3701 .chain(snapshot.all_weak.iter())
3702}
3703
3704fn close_open_upvalues_for_unreachable_threads(
3705 global: &GlobalState,
3706 marker: &mut lua_gc::Marker,
3707) {
3708 use lua_gc::Trace;
3709
3710 let mut closed_values = Vec::<LuaValue>::new();
3711 for (id, entry) in global.threads.iter() {
3712 if entry.value.id != *id {
3713 continue;
3714 }
3715 if thread_entry_marked_alive(marker, *id, entry) {
3716 continue;
3717 }
3718 let Ok(thread) = entry.state.try_borrow() else {
3719 continue;
3720 };
3721 for uv in thread.openupval.iter() {
3722 if !marker.is_visited(uv.identity()) {
3723 continue;
3724 }
3725 let Some((thread_id, idx)) = uv.try_open_payload() else {
3726 continue;
3727 };
3728 if thread_id as u64 != *id {
3729 continue;
3730 }
3731 let value = thread.get_at(idx);
3732 uv.close_with(value.clone());
3733 closed_values.push(value);
3734 }
3735 }
3736 for value in closed_values {
3737 value.trace(marker);
3738 }
3739 marker.drain_gray_queue();
3740}
3741
3742fn record_live_interned_strings(
3743 global: &GlobalState,
3744 marker: &lua_gc::Marker,
3745 live_ids: &std::cell::RefCell<std::collections::HashSet<usize>>,
3746) {
3747 let mut live = live_ids.borrow_mut();
3748 for s in global.interned_lt.values() {
3749 let id = s.identity();
3750 if marker.is_visited(id) {
3751 live.insert(id);
3752 }
3753 }
3754}
3755
3756fn retain_live_interned_strings(
3757 global: &mut GlobalState,
3758 live_ids: &std::collections::HashSet<usize>,
3759) {
3760 global
3761 .interned_lt
3762 .retain(|_, s| live_ids.contains(&s.identity()));
3763}
3764
3765impl<'a> GcHandle<'a> {
3766 pub fn check_step(&self) {
3773 if !self._state.global().is_gc_running() {
3774 return;
3775 }
3776 if self._state.global().is_gen_mode() {
3777 let should_collect = {
3778 let g = self._state.global();
3779 g.heap.would_collect() || g.gc_debt() > 0
3780 };
3781 if should_collect {
3782 self.generational_step();
3783 }
3784 } else {
3785 self.collect_via_heap(false);
3786 }
3787 }
3788
3789 pub fn full_collect(&self) {
3791 if self._state.global().is_gen_mode() {
3792 self.fullgen();
3793 } else {
3794 self.collect_via_heap(true);
3795 }
3796 }
3797
3798 fn negative_debt(bytes: usize) -> isize {
3799 -(bytes.min(isize::MAX as usize) as isize)
3800 }
3801
3802 fn set_minor_debt(&self) {
3803 let mut g = self._state.global_mut();
3804 let total = g.total_bytes();
3805 let growth = (total / 100).saturating_mul(g.genminormul as usize);
3806 g.heap
3807 .set_threshold_bytes(total.saturating_add(growth.max(1)));
3808 set_debt(&mut *g, Self::negative_debt(growth));
3809 }
3810
3811 fn set_pause_debt(&self) {
3812 let mut g = self._state.global_mut();
3813 let total = g.total_bytes();
3814 let pause = g.gc_pause_param().max(0) as usize;
3815 let threshold = g.gc_estimate.max(1).saturating_mul(pause) / 100;
3816 let debt = if threshold > total {
3817 Self::negative_debt(threshold - total)
3818 } else {
3819 0
3820 };
3821 let heap_threshold = if threshold > total {
3822 threshold
3823 } else {
3824 total.saturating_add(1)
3825 };
3826 g.heap.set_threshold_bytes(heap_threshold);
3827 set_debt(&mut *g, debt);
3828 }
3829
3830 fn enter_incremental_mode(&self) {
3831 let mut g = self._state.global_mut();
3832 g.heap.reset_all_ages();
3833 g.finalizers.reset_generation_boundaries();
3834 g.gckind = GcKind::Incremental as u8;
3835 g.lastatomic = 0;
3836 }
3837
3838 fn enter_generational_mode(&self) -> usize {
3839 self.collect_via_heap_mode(HeapCollectMode::Full);
3840 let numobjs = {
3841 let mut g = self._state.global_mut();
3842 g.heap.promote_all_to_old();
3843 g.finalizers.promote_all_pending_to_old();
3844 g.heap.allgc_count()
3845 };
3846 let total = self._state.global().total_bytes();
3847 {
3848 let mut g = self._state.global_mut();
3849 g.gckind = GcKind::Generational as u8;
3850 g.lastatomic = 0;
3851 g.gc_estimate = total;
3852 }
3853 self.set_minor_debt();
3854 numobjs
3855 }
3856
3857 fn fullgen(&self) -> usize {
3858 self.enter_incremental_mode();
3859 self.enter_generational_mode()
3860 }
3861
3862 fn stepgenfull(&self, lastatomic: usize) {
3863 if self._state.global().gckind == GcKind::Generational as u8 {
3864 self.enter_incremental_mode();
3865 }
3866 self.collect_via_heap_mode(HeapCollectMode::Full);
3867 let newatomic = self._state.global().heap.allgc_count().max(1);
3868 if newatomic < lastatomic.saturating_add(lastatomic >> 3) {
3869 {
3870 let mut g = self._state.global_mut();
3871 g.heap.promote_all_to_old();
3872 g.finalizers.promote_all_pending_to_old();
3873 }
3874 let total = self._state.global().total_bytes();
3875 {
3876 let mut g = self._state.global_mut();
3877 g.gckind = GcKind::Generational as u8;
3878 g.lastatomic = 0;
3879 g.gc_estimate = total;
3880 }
3881 self.set_minor_debt();
3882 } else {
3883 {
3884 let mut g = self._state.global_mut();
3885 g.heap.reset_all_ages();
3886 g.finalizers.reset_generation_boundaries();
3887 }
3888 let total = self._state.global().total_bytes();
3889 {
3890 let mut g = self._state.global_mut();
3891 g.gckind = GcKind::Incremental as u8;
3892 g.lastatomic = newatomic;
3893 g.gc_estimate = total;
3894 }
3895 self.set_pause_debt();
3896 }
3897 }
3898
3899 fn collect_via_heap(&self, force: bool) {
3908 self.collect_via_heap_mode(if force {
3909 HeapCollectMode::Full
3910 } else {
3911 HeapCollectMode::Step
3912 });
3913 }
3914
3915 fn collect_via_heap_mode(&self, mode: HeapCollectMode) {
3916 use lua_gc::Trace;
3917 let state_ref: &LuaState = &*self._state;
3918
3919 if matches!(mode, HeapCollectMode::Step) {
3925 let g = state_ref.global.borrow();
3926 if !g.heap.would_collect() {
3927 return;
3928 }
3929 }
3930
3931 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
3935 let mut g = state_ref.global.borrow_mut();
3936 g.weak_tables_registry.live_snapshot_by_kind()
3937 };
3938
3939 let pending_snapshot: Vec<FinalizerObject> = {
3944 let g = state_ref.global.borrow();
3945 match mode {
3946 HeapCollectMode::Minor => g.finalizers.pending_minor_snapshot(),
3947 HeapCollectMode::Full | HeapCollectMode::Step => g.finalizers.pending_snapshot(),
3948 }
3949 };
3950
3951 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
3952 std::cell::RefCell::new(std::collections::HashSet::new());
3953 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
3954 std::cell::RefCell::new(Vec::new());
3955 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
3956 std::cell::RefCell::new(std::collections::HashSet::new());
3957 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
3958 std::cell::RefCell::new(std::collections::HashSet::new());
3959 let live_interned_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
3960 std::cell::RefCell::new(std::collections::HashSet::new());
3961 let collect_ran = std::cell::Cell::new(false);
3962
3963 {
3964 let global = state_ref.global.borrow();
3965 global.heap.unpause();
3966 let roots = CollectRoots { global: &*global, thread: state_ref };
3967 let hook = |marker: &mut lua_gc::Marker| {
3968 collect_ran.set(true);
3969 trace_reachable_threads(&*global, global.current_thread_id, marker);
3970 close_open_upvalues_for_unreachable_threads(&*global, marker);
3971 loop {
3972 let visited_before = marker.visited_count();
3973 for t in &weak_tables_snapshot.ephemeron {
3974 if !marker.is_marked_or_old(t.0) {
3975 continue;
3976 }
3977 let to_mark = t.ephemeron_values_to_mark_with_value(
3978 &|v| lua_value_marked_or_old(marker, v),
3979 );
3980 for v in &to_mark {
3981 v.trace(marker);
3982 }
3983 }
3984 marker.drain_gray_queue();
3985 if marker.visited_count() == visited_before {
3986 break;
3987 }
3988 }
3989 for pf in &pending_snapshot {
3990 if !finalizer_marked_or_old(marker, pf) {
3991 pf.mark(marker);
3992 finalizing_ids.borrow_mut().insert(pf.identity());
3993 newly_unreachable.borrow_mut().push(pf.clone());
3994 }
3995 }
3996 marker.drain_gray_queue();
3997 loop {
3998 let visited_before = marker.visited_count();
3999 for t in &weak_tables_snapshot.ephemeron {
4000 if !marker.is_marked_or_old(t.0) {
4001 continue;
4002 }
4003 let to_mark = t.ephemeron_values_to_mark_with_value(
4004 &|v| lua_value_marked_or_old(marker, v),
4005 );
4006 for v in &to_mark {
4007 v.trace(marker);
4008 }
4009 }
4010 marker.drain_gray_queue();
4011 if marker.visited_count() == visited_before {
4012 break;
4013 }
4014 }
4015 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4016 let id = t.identity();
4017 if marker.is_marked_or_old(t.0) {
4018 let to_mark = {
4019 let finalizing = finalizing_ids.borrow();
4020 t.prune_weak_dead_with_value(
4021 &|v| lua_value_marked_or_old(marker, v),
4022 &|v| {
4023 lua_value_marked_or_old(marker, v)
4024 && lua_value_identity(v)
4025 .map_or(true, |id| !finalizing.contains(&id))
4026 },
4027 )
4028 };
4029 for v in &to_mark {
4030 v.trace(marker);
4031 }
4032 alive_ids.borrow_mut().insert(id);
4033 }
4034 }
4035 marker.drain_gray_queue();
4036 {
4037 let mut alive = alive_thread_ids.borrow_mut();
4038 for (id, entry) in global.threads.iter() {
4039 if thread_entry_marked_alive(marker, *id, entry) {
4040 alive.insert(*id);
4041 }
4042 }
4043 }
4044 record_live_interned_strings(&*global, marker, &live_interned_ids);
4045 };
4046 match mode {
4047 HeapCollectMode::Full => global.heap.full_collect_with_post_mark(&roots, hook),
4048 HeapCollectMode::Step => global.heap.step_with_post_mark(&roots, hook),
4049 HeapCollectMode::Minor => global.heap.minor_collect_with_post_mark(&roots, hook),
4050 }
4051 }
4052
4053 if !collect_ran.get() {
4054 return;
4055 }
4056
4057 let alive_set = alive_ids.into_inner();
4061 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4062 let alive_thread_ids = alive_thread_ids.into_inner();
4063 let live_interned_ids = live_interned_ids.into_inner();
4064 let mut g = state_ref.global.borrow_mut();
4065 retain_live_interned_strings(&mut *g, &live_interned_ids);
4066 g.weak_tables_registry.retain_identities(&alive_set);
4067 let main_thread_id = g.main_thread_id;
4068 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4069 g.cross_thread_upvals
4070 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4071 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4075 for object in &promoted {
4076 if let Some(ptr) = object.heap_ptr() {
4077 g.heap.move_finobj_to_tobefnz(ptr);
4078 }
4079 }
4080 if matches!(mode, HeapCollectMode::Minor) {
4081 g.finalizers.finish_minor_collection();
4082 }
4083 }
4084
4085 pub fn generational_step(&self) -> bool {
4087 self.generational_step_with_major(true)
4088 }
4089
4090 pub fn generational_step_minor_only(&self) -> bool {
4096 self.generational_step_with_major(false)
4097 }
4098
4099 fn generational_step_with_major(&self, allow_major: bool) -> bool {
4100 let (lastatomic, majorbase, majorinc, should_major) = {
4101 let g = self._state.global();
4102 let majorbase = if g.gc_estimate == 0 {
4103 g.total_bytes()
4104 } else {
4105 g.gc_estimate
4106 };
4107 let majormul = g.gc_genmajormul_param().max(0) as usize;
4108 let majorinc = (majorbase / 100).saturating_mul(majormul);
4109 let debt_due = g.gc_debt() > 0 || g.heap.would_collect();
4110 let should_major = allow_major
4111 && debt_due
4112 && g.total_bytes() > majorbase.saturating_add(majorinc);
4113 (g.lastatomic, majorbase, majorinc, should_major)
4114 };
4115
4116 if lastatomic != 0 {
4117 self.stepgenfull(lastatomic);
4118 debug_assert!(self._state.global().is_gen_mode());
4119 return true;
4120 }
4121
4122 if should_major {
4123 let numobjs = self.fullgen();
4124 let after = self._state.global().total_bytes();
4125 if after < majorbase.saturating_add(majorinc / 2) {
4126 self.set_minor_debt();
4127 } else {
4128 {
4129 let mut g = self._state.global_mut();
4130 g.lastatomic = numobjs.max(1);
4131 }
4132 self.set_pause_debt();
4133 }
4134 } else {
4135 self.collect_via_heap_mode(HeapCollectMode::Minor);
4136 self.set_minor_debt();
4137 self._state.global_mut().gc_estimate = majorbase;
4138 }
4139
4140 debug_assert!(self._state.global().is_gen_mode());
4141 true
4142 }
4143
4144 pub fn step(&self) { }
4146
4147 pub fn incremental_step(&self, work_units: isize) -> bool {
4160 self.incremental_step_to_state(work_units, None)
4161 }
4162
4163 pub fn run_until_gc_state_for_test(&self, target: lua_gc::GcState) -> bool {
4168 self.incremental_step_to_state(isize::MAX / 4, Some(target));
4169 self._state.global().heap.gc_state() == target
4170 }
4171
4172 fn incremental_step_to_state(
4173 &self,
4174 work_units: isize,
4175 target: Option<lua_gc::GcState>,
4176 ) -> bool {
4177 use lua_gc::{StepBudget, StepOutcome, Trace};
4178 let state_ref: &LuaState = &*self._state;
4179
4180 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4181 let mut g = state_ref.global.borrow_mut();
4182 g.weak_tables_registry.live_snapshot_by_kind()
4183 };
4184
4185 let pending_snapshot: Vec<FinalizerObject> = {
4186 let g = state_ref.global.borrow();
4187 g.finalizers.pending_snapshot()
4188 };
4189
4190 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4191 std::cell::RefCell::new(std::collections::HashSet::new());
4192 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4193 std::cell::RefCell::new(Vec::new());
4194 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4195 std::cell::RefCell::new(std::collections::HashSet::new());
4196 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4197 std::cell::RefCell::new(std::collections::HashSet::new());
4198 let live_interned_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4199 std::cell::RefCell::new(std::collections::HashSet::new());
4200 let atomic_ran = std::cell::Cell::new(false);
4201
4202 let stop_target = {
4203 let g = state_ref.global.borrow();
4204 match (target, g.heap.gc_state()) {
4205 (Some(target), _) => Some(target),
4206 (None, lua_gc::GcState::CallFin) => None,
4207 (None, _) => Some(lua_gc::GcState::CallFin),
4208 }
4209 };
4210
4211 let outcome = {
4212 let global = state_ref.global.borrow();
4213 global.heap.unpause();
4214 let roots = CollectRoots { global: &*global, thread: state_ref };
4215 let hook = |marker: &mut lua_gc::Marker| {
4216 atomic_ran.set(true);
4217 trace_reachable_threads(&*global, global.current_thread_id, marker);
4218 close_open_upvalues_for_unreachable_threads(&*global, marker);
4219 loop {
4220 let visited_before = marker.visited_count();
4221 for t in &weak_tables_snapshot.ephemeron {
4222 let t_id = t.identity();
4223 if !marker.is_visited(t_id) {
4224 continue;
4225 }
4226 let to_mark = t.ephemeron_values_to_mark(
4227 &|id| marker.is_visited(id),
4228 );
4229 for v in &to_mark {
4230 v.trace(marker);
4231 }
4232 }
4233 marker.drain_gray_queue();
4234 if marker.visited_count() == visited_before {
4235 break;
4236 }
4237 }
4238 for pf in &pending_snapshot {
4239 if !marker.is_visited(pf.identity()) {
4240 pf.mark(marker);
4241 finalizing_ids.borrow_mut().insert(pf.identity());
4242 newly_unreachable.borrow_mut().push(pf.clone());
4243 }
4244 }
4245 marker.drain_gray_queue();
4246 loop {
4247 let visited_before = marker.visited_count();
4248 for t in &weak_tables_snapshot.ephemeron {
4249 let t_id = t.identity();
4250 if !marker.is_visited(t_id) {
4251 continue;
4252 }
4253 let to_mark = t.ephemeron_values_to_mark(
4254 &|id| marker.is_visited(id),
4255 );
4256 for v in &to_mark {
4257 v.trace(marker);
4258 }
4259 }
4260 marker.drain_gray_queue();
4261 if marker.visited_count() == visited_before {
4262 break;
4263 }
4264 }
4265 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4266 let id = t.identity();
4267 if marker.is_visited(id) {
4268 let to_mark = {
4269 let finalizing = finalizing_ids.borrow();
4270 t.prune_weak_dead_with(
4271 &|id| marker.is_visited(id),
4272 &|id| marker.is_visited(id) && !finalizing.contains(&id),
4273 )
4274 };
4275 for v in &to_mark {
4276 v.trace(marker);
4277 }
4278 alive_ids.borrow_mut().insert(id);
4279 }
4280 }
4281 marker.drain_gray_queue();
4282 {
4283 let mut alive = alive_thread_ids.borrow_mut();
4284 for (id, entry) in global.threads.iter() {
4285 if thread_entry_marked_alive(marker, *id, entry) {
4286 alive.insert(*id);
4287 }
4288 }
4289 }
4290 record_live_interned_strings(&*global, marker, &live_interned_ids);
4291 };
4292 let budget = StepBudget::from_work(work_units);
4293 if let Some(target) = stop_target {
4294 global.heap.incremental_run_until_state_with_post_mark(
4295 &roots,
4296 target,
4297 work_units,
4298 hook,
4299 )
4300 } else {
4301 global.heap.incremental_step_with_post_mark(&roots, budget, hook)
4302 }
4303 };
4304
4305 if atomic_ran.get() {
4306 let alive_set = alive_ids.into_inner();
4307 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4308 let alive_thread_ids = alive_thread_ids.into_inner();
4309 let live_interned_ids = live_interned_ids.into_inner();
4310 let mut g = state_ref.global.borrow_mut();
4311 retain_live_interned_strings(&mut *g, &live_interned_ids);
4312 g.weak_tables_registry.retain_identities(&alive_set);
4313 let main_thread_id = g.main_thread_id;
4314 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4315 g.cross_thread_upvals
4316 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4317 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4318 for object in &promoted {
4319 if let Some(ptr) = object.heap_ptr() {
4320 g.heap.move_finobj_to_tobefnz(ptr);
4321 }
4322 }
4323 }
4324
4325 let mut paused = matches!(outcome, StepOutcome::Paused);
4326 if target.is_none()
4327 && self._state.global().heap.gc_state() == lua_gc::GcState::CallFin
4328 && !self._state.global().finalizers.has_to_be_finalized()
4329 {
4330 paused = self._state.global().heap.finish_callfin_phase();
4331 }
4332
4333 paused
4334 }
4335
4336 pub fn prune_weak_tables_mark_only(&self) {
4343 use lua_gc::Trace;
4344 let state_ref: &LuaState = &*self._state;
4345
4346 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4347 let mut g = state_ref.global.borrow_mut();
4348 g.weak_tables_registry.live_snapshot_by_kind()
4349 };
4350
4351 let live_interned_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4352 std::cell::RefCell::new(std::collections::HashSet::new());
4353
4354 {
4355 let global = state_ref.global.borrow();
4356 global.heap.unpause();
4357 let roots = CollectRoots { global: &*global, thread: state_ref };
4358 let hook = |marker: &mut lua_gc::Marker| {
4359 trace_reachable_threads(&*global, global.current_thread_id, marker);
4360 loop {
4361 let visited_before = marker.visited_count();
4362 for t in &weak_tables_snapshot.ephemeron {
4363 let t_id = t.identity();
4364 if !marker.is_visited(t_id) {
4365 continue;
4366 }
4367 let to_mark = t.ephemeron_values_to_mark(
4368 &|id| marker.is_visited(id),
4369 );
4370 for v in &to_mark {
4371 v.trace(marker);
4372 }
4373 }
4374 marker.drain_gray_queue();
4375 if marker.visited_count() == visited_before {
4376 break;
4377 }
4378 }
4379 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4380 if marker.is_visited(t.identity()) {
4381 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4382 for v in &to_mark {
4383 v.trace(marker);
4384 }
4385 }
4386 }
4387 marker.drain_gray_queue();
4388 record_live_interned_strings(&*global, marker, &live_interned_ids);
4389 };
4390 global.heap.mark_only_with_post_mark(&roots, hook);
4391 }
4392
4393 let live_interned_ids = live_interned_ids.into_inner();
4394 let mut g = state_ref.global.borrow_mut();
4395 retain_live_interned_strings(&mut *g, &live_interned_ids);
4396 }
4397
4398 pub fn change_mode(&self, mode: GcKind) {
4400 let old = self._state.global().gckind;
4401 if old == mode as u8 {
4402 self._state.global_mut().lastatomic = 0;
4403 return;
4404 }
4405 match mode {
4406 GcKind::Generational => {
4407 self.enter_generational_mode();
4408 }
4409 GcKind::Incremental => {
4410 self.enter_incremental_mode();
4411 }
4412 }
4413 }
4414
4415 pub fn fix_object<T: lua_gc::Trace + 'static>(&self, _o: &GcRef<T>) { }
4417
4418 pub fn free_all_objects(&self) {
4422 }
4424
4425 pub fn barrier(&self, p: &dyn std::any::Any, v: &LuaValue) {
4429 let g = self._state.global();
4430 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Forward);
4431 }
4432
4433 pub fn barrier_back(&self, p: &dyn std::any::Any, v: &LuaValue) {
4437 let g = self._state.global();
4438 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Backward);
4439 }
4440
4441 pub fn obj_barrier(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4445 let g = self._state.global();
4446 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Forward);
4447 }
4448
4449 pub fn obj_barrier_back(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4452 let g = self._state.global();
4453 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Backward);
4454 }
4455}
4456
4457fn make_seed() -> u32 {
4466 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4467 {
4468 return crate::string::hash_bytes(b"lua-rs-wasm-seed", 0x9e37_79b9);
4469 }
4470
4471 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
4472 {
4473 use std::time::{SystemTime, UNIX_EPOCH};
4474 let t = SystemTime::now()
4475 .duration_since(UNIX_EPOCH)
4476 .map(|d| d.as_secs() as u32)
4477 .unwrap_or(0);
4478
4479 crate::string::hash_bytes(&t.to_le_bytes(), t)
4487 }
4488}
4489
4490pub(crate) fn set_debt(g: &mut GlobalState, mut debt: isize) {
4504 let tb = g.total_bytes() as isize;
4505 debug_assert!(tb > 0);
4506 if debt < tb.saturating_sub(isize::MAX) {
4508 debt = tb - isize::MAX;
4509 }
4510 g.gc_debt = debt;
4511}
4512
4513pub fn set_c_stack_limit(_state: &mut LuaState, _limit: u32) -> i32 {
4523 let _ = (_state, _limit);
4524 LUAI_MAXCCALLS as i32
4525}
4526
4527pub(crate) fn extend_ci(state: &mut LuaState) -> CallInfoIdx {
4544 debug_assert!(
4545 state.call_info[state.ci.0 as usize].next.is_none(),
4546 "extend_ci: current ci already has a cached next frame"
4547 );
4548
4549 let current_idx = state.ci;
4550 let new_idx = CallInfoIdx(state.call_info.len() as u32);
4552
4553 state.call_info.push(CallInfo {
4554 previous: Some(current_idx),
4555 next: None,
4556 u: CallInfoFrame::lua_default(),
4557 ..CallInfo::default()
4558 });
4559
4560 state.call_info[current_idx.0 as usize].next = Some(new_idx);
4561
4562 state.nci += 1;
4563
4564 new_idx
4565}
4566
4567fn free_ci(state: &mut LuaState) {
4588 let ci_idx = state.ci.0 as usize;
4589
4590 let mut next_opt = state.call_info[ci_idx].next.take();
4591
4592 while let Some(idx) = next_opt {
4593 next_opt = state.call_info[idx.0 as usize].next;
4594 state.nci = state.nci.saturating_sub(1);
4595 }
4596
4597 state.call_info.truncate(ci_idx + 1);
4600}
4601
4602pub(crate) fn shrink_ci(state: &mut LuaState) {
4629 let ci_idx = state.ci.0 as usize;
4630
4631 if state.call_info[ci_idx].next.is_none() {
4632 return;
4633 }
4634
4635 let free_count = state.call_info.len().saturating_sub(ci_idx + 1);
4636 if free_count <= 1 {
4637 return;
4638 }
4639
4640 let keep = free_count / 2;
4644 let removed = free_count - keep;
4645 let new_len = ci_idx + 1 + keep;
4646 state.call_info.truncate(new_len);
4647 state.nci = state.nci.saturating_sub(removed as u32);
4648
4649 if let Some(last) = state.call_info.last_mut() {
4651 last.next = None;
4652 }
4653}
4654
4655pub(crate) fn check_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4667 if state.c_calls() == LUAI_MAXCCALLS {
4670 return Err(LuaError::runtime(format_args!("C stack overflow")));
4671 }
4672 if state.c_calls() >= (LUAI_MAXCCALLS / 10 * 11) {
4674 return Err(LuaError::runtime(format_args!(
4677 "error while handling stack overflow (C stack overflow)"
4678 )));
4679 }
4680 Ok(())
4681}
4682
4683pub fn inc_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4694 state.n_ccalls += 1;
4695 if state.c_calls() >= LUAI_MAXCCALLS {
4697 check_c_stack(state)?;
4698 }
4699 Ok(())
4700}
4701
4702fn stack_init(thread: &mut LuaState) {
4708 let total_slots = BASIC_STACK_SIZE + EXTRA_STACK;
4710 thread.stack = vec![StackValue::default(); total_slots];
4711
4712 thread.tbclist = Vec::new();
4716
4717 thread.top = StackIdx(0);
4722
4723 thread.stack_last = StackIdx(BASIC_STACK_SIZE as u32);
4724
4725
4726 let base_ci = CallInfo {
4727 func: StackIdx(0),
4728 top: StackIdx(1 + LUA_MINSTACK as u32),
4729 previous: None,
4730 next: None,
4731 callstatus: CIST_C,
4732 nresults: 0,
4733 u: CallInfoFrame::c_default(),
4734 u2: CallInfoExtra::default(),
4735 };
4736
4737 if thread.call_info.is_empty() {
4738 thread.call_info.push(base_ci);
4739 } else {
4740 thread.call_info[0] = base_ci;
4741 thread.call_info.truncate(1);
4742 }
4743
4744 thread.stack[0] = StackValue { val: LuaValue::Nil, tbc_delta: 0 };
4745
4746 thread.top = StackIdx(1);
4747
4748 thread.ci = CallInfoIdx(0);
4749}
4750
4751fn free_stack(state: &mut LuaState) {
4752 if state.stack.is_empty() {
4753 return;
4754 }
4755 state.ci = CallInfoIdx(0);
4756 free_ci(state);
4757 debug_assert_eq!(state.nci, 0, "nci should be 0 after free_ci");
4758 state.stack.clear();
4760 state.stack.shrink_to_fit();
4761}
4762
4763fn init_registry(state: &mut LuaState) -> Result<(), LuaError> {
4764 let registry = state.new_table();
4766
4767 state.global_mut().l_registry = LuaValue::Table(registry.clone());
4769
4770 let globals = state.new_table();
4790 state.global_mut().globals = LuaValue::Table(globals);
4791 let loaded = state.new_table();
4792 state.global_mut().loaded = LuaValue::Table(loaded);
4793
4794 Ok(())
4795}
4796
4797fn lua_open(state: &mut LuaState) -> Result<(), LuaError> {
4798 stack_init(state);
4799 init_registry(state)?;
4800 crate::string::init(state)?;
4801 crate::tagmethods::init(state)?;
4802 state.global_mut().gcstp = 0;
4804 state.global().heap.unpause();
4805 state.global_mut().nilvalue = LuaValue::Nil;
4808 Ok(())
4810}
4811
4812fn preinit_thread(thread: &mut LuaState, global: Rc<RefCell<GlobalState>>) {
4813 thread.global = global;
4814 thread.stack = Vec::new();
4815 thread.call_info = Vec::new();
4816 thread.ci = CallInfoIdx(0);
4819 thread.nci = 0;
4820 thread.n_ccalls = 0;
4824 thread.hook = None;
4825 thread.hookmask = 0;
4826 thread.basehookcount = 0;
4827 thread.allowhook = true;
4828 thread.hookcount = thread.basehookcount;
4830
4831 {
4836 let (active, interval) = {
4837 let g = thread.global.borrow();
4838 (g.sandbox_active(), g.sandbox.interval.get())
4839 };
4840 if active {
4841 thread.hookmask = SANDBOX_COUNT_MASK;
4842 thread.basehookcount = interval;
4843 thread.hookcount = interval;
4844 }
4845 }
4846 thread.openupval = Vec::new();
4847 thread.status = LuaStatus::Ok as u8;
4848 thread.errfunc = 0;
4849 thread.oldpc = 0;
4850 thread.gc_check_needed = true;
4851}
4852
4853fn close_state(state: &mut LuaState) {
4854 let is_complete = state.global().is_complete();
4855
4856 if !is_complete {
4857 state.gc().free_all_objects();
4859 } else {
4860 state.ci = CallInfoIdx(0);
4861 state.gc().free_all_objects();
4864 }
4866
4867 state.global_mut().strt = StringPool::default();
4869
4870 free_stack(state);
4871
4872 }
4877
4878pub fn new_thread(state: &mut LuaState, initial_body: Option<LuaValue>) -> Result<(), LuaError> {
4907 state.gc().check_step();
4908
4909 let global_rc = state.global_rc();
4914 let hookmask = state.hookmask;
4915 let basehookcount = state.basehookcount;
4916
4917 let reserved_id = {
4918 let mut g = state.global_mut();
4919 let id = g.next_thread_id;
4920 g.next_thread_id += 1;
4921 id
4922 };
4923
4924 let mut new_thread = LuaState {
4925 status: LuaStatus::Ok as u8,
4926 allowhook: true,
4927 nci: 0,
4928 top: StackIdx(0),
4929 stack_last: StackIdx(0),
4930 stack: Vec::new(),
4931 ci: CallInfoIdx(0),
4932 call_info: Vec::new(),
4933 openupval: Vec::new(),
4934 tbclist: Vec::new(),
4935 global: global_rc.clone(),
4936 hook: None,
4937 hookmask: 0,
4938 basehookcount: 0,
4939 hookcount: 0,
4940 errfunc: 0,
4941 n_ccalls: 0,
4942 oldpc: 0,
4943 marked: 0,
4944 cached_thread_id: reserved_id,
4945 gc_check_needed: false,
4946 };
4947
4948 preinit_thread(&mut new_thread, global_rc);
4949
4950 new_thread.hookmask = hookmask;
4951 new_thread.basehookcount = basehookcount;
4952 new_thread.reset_hook_count();
4955
4956 stack_init(&mut new_thread);
4962
4963 if let Some(body) = initial_body {
4964 new_thread.push(body);
4965 }
4966
4967 let thread_ref: Rc<RefCell<LuaState>> = Rc::new(RefCell::new(new_thread));
4968
4969 let value = {
4970 let mut g = state.global_mut();
4971 let id = reserved_id;
4972 let value = GcRef::new(lua_types::value::LuaThread::new(id));
4973 g.threads.insert(
4974 id,
4975 ThreadRegistryEntry { state: thread_ref, value: value.clone() },
4976 );
4977 value
4978 };
4979
4980 state.push(LuaValue::Thread(value));
4981
4982 Ok(())
4983}
4984
4985pub fn reset_thread(state: &mut LuaState, status: i32) -> i32 {
5007 state.ci = CallInfoIdx(0);
5008 let ci_idx = 0usize;
5009
5010 if !state.stack.is_empty() {
5012 state.stack[0].val = LuaValue::Nil;
5013 }
5014
5015 state.call_info[ci_idx].func = StackIdx(0);
5016 state.call_info[ci_idx].callstatus = CIST_C;
5017
5018 let mut status = if status == LuaStatus::Yield as i32 {
5019 LuaStatus::Ok as i32
5020 } else {
5021 status
5022 };
5023
5024 state.status = LuaStatus::Ok as u8;
5025
5026 let close_status = crate::do_::close_protected(
5027 state,
5028 StackIdx(1),
5029 LuaStatus::from_raw(status),
5030 );
5031 status = close_status as i32;
5032
5033 if status != LuaStatus::Ok as i32 {
5034 crate::do_::set_error_obj(state, LuaStatus::from_raw(status), StackIdx(1));
5035 } else {
5036 state.top = StackIdx(1);
5037 }
5038
5039 let new_ci_top = StackIdx(state.top.0 + LUA_MINSTACK as u32);
5040 state.call_info[ci_idx].top = new_ci_top;
5041
5042 let needed = new_ci_top.0 as usize;
5045 if state.stack.len() < needed {
5046 state.stack.resize(needed, StackValue::default());
5047 }
5048
5049 status
5050}
5051
5052pub fn close_thread(state: &mut LuaState, from: Option<&LuaState>) -> i32 {
5066 state.n_ccalls = match from {
5068 Some(f) => f.c_calls(),
5069 None => 0,
5070 };
5071 let current_status = state.status as i32;
5072 let result = reset_thread(state, current_status);
5073 result
5074}
5075
5076pub fn reset_thread_api(state: &mut LuaState) -> i32 {
5085 close_thread(state, None)
5086}
5087
5088pub fn new_state() -> Option<LuaState> {
5124 let placeholder_str = GcRef::new(LuaString::placeholder());
5133
5134 let initial_white = 1u8 << WHITE0BIT;
5136
5137 let global = GlobalState {
5141 parser_hook: None,
5142 cli_argv: None,
5143 cli_preload: None,
5144 lua_version: lua_types::LuaVersion::default(),
5145 file_loader_hook: None,
5146 file_open_hook: None,
5147 stdout_hook: None,
5148 stderr_hook: None,
5149 stdin_hook: None,
5150 env_hook: None,
5151 unix_time_hook: None,
5152 cpu_clock_hook: None,
5153 local_offset_hook: None,
5154 entropy_hook: None,
5155 temp_name_hook: None,
5156 popen_hook: None,
5157 file_remove_hook: None,
5158 file_rename_hook: None,
5159 os_execute_hook: None,
5160 dynlib_load_hook: None,
5161 dynlib_symbol_hook: None,
5162 dynlib_unload_hook: None,
5163 sandbox: SandboxLimits::default(),
5164 gc_debt: 0,
5165 gc_estimate: 0,
5166 lastatomic: 0,
5167 strt: StringPool::default(),
5168 l_registry: LuaValue::Nil,
5169 external_roots: ExternalRootSet::default(),
5170 globals: LuaValue::Nil,
5171 loaded: LuaValue::Nil,
5172 nilvalue: LuaValue::Int(0),
5173 seed: make_seed(),
5174 currentwhite: initial_white,
5175 gcstate: GCS_PAUSE,
5176 gckind: GcKind::Incremental as u8,
5178 gcstopem: false,
5179 genminormul: LUAI_GENMINORMUL,
5180 genmajormul: (LUAI_GENMAJORMUL / 4) as u8,
5182 gcstp: GCSTPGC,
5183 gcemergency: false,
5184 gcpause: (LUAI_GCPAUSE / 4) as u8,
5185 gcstepmul: (LUAI_GCMUL / 4) as u8,
5186 gcstepsize: LUAI_GCSTEPSIZE,
5187 gc55_params: [20, 50, 68, 250, 200, 9600],
5190 sweepgc_cursor: 0,
5191 weak_tables_registry: lua_gc::WeakRegistry::default(),
5192 finalizers: lua_gc::FinalizerRegistry::default(),
5193 gc_finalizer_error: None,
5194 twups: Vec::new(),
5195 panic: None,
5196 mainthread: None,
5197 threads: std::collections::HashMap::new(),
5198 main_thread_value: GcRef::new(lua_types::value::LuaThread::new(0)),
5199 current_thread_id: 0,
5200 main_thread_id: 0,
5201 next_thread_id: 1,
5202 memerrmsg: placeholder_str.clone(),
5203 tmname: Vec::new(),
5204 mt: std::array::from_fn(|_| None),
5205 strcache: std::array::from_fn(|_| {
5206 std::array::from_fn(|_| placeholder_str.clone())
5207 }),
5208 interned_lt: std::collections::HashMap::new(),
5209 warnf: None,
5210 warn_mode: WarnMode::Off,
5211 test_warn_enabled: false,
5212 test_warn_on: false,
5213 test_warn_mode: TestWarnMode::Normal,
5214 test_warn_last_to_cont: false,
5215 test_warn_buffer: Vec::new(),
5216 c_functions: Vec::new(),
5217 heap: lua_gc::Heap::new(),
5218 cross_thread_upvals: std::collections::HashMap::new(),
5219 suspended_parent_stacks: Vec::new(),
5220 suspended_parent_open_upvals: Vec::new(),
5221 };
5222
5223 let global_rc = Rc::new(RefCell::new(global));
5224
5225 let initial_marked = initial_white;
5227
5228 let mut main_thread = LuaState {
5229 status: LuaStatus::Ok as u8,
5230 allowhook: true,
5231 nci: 0,
5232 top: StackIdx(0),
5233 stack_last: StackIdx(0),
5234 stack: Vec::new(),
5235 ci: CallInfoIdx(0),
5236 call_info: Vec::new(),
5237 openupval: Vec::new(),
5238 tbclist: Vec::new(),
5239 global: global_rc.clone(),
5240 hook: None,
5241 hookmask: 0,
5242 basehookcount: 0,
5243 hookcount: 0,
5244 errfunc: 0,
5245 n_ccalls: 0,
5246 oldpc: 0,
5247 marked: initial_marked,
5248 cached_thread_id: 0,
5249 gc_check_needed: false,
5250 };
5251
5252 preinit_thread(&mut main_thread, global_rc.clone());
5253
5254 main_thread.inc_nny();
5256
5257 match lua_open(&mut main_thread) {
5267 Ok(()) => {}
5268 Err(_) => {
5269 close_state(&mut main_thread);
5270 return None;
5271 }
5272 }
5273
5274 Some(main_thread)
5275}
5276
5277pub fn close(mut state: LuaState) {
5293 close_state(&mut state);
5298}
5299
5300pub(crate) fn warning(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5310 let test_warn_enabled = state.global().test_warn_enabled;
5311 if test_warn_enabled {
5312 test_warn(state, msg, to_cont);
5313 return;
5314 }
5315
5316 let has_warnf = state.global().warnf.is_some();
5325 if has_warnf {
5326 let mut warnf = state.global_mut().warnf.take();
5328 if let Some(ref mut f) = warnf {
5329 f(msg, to_cont);
5330 }
5331 state.global_mut().warnf = warnf;
5333 return;
5334 }
5335 default_warn(state, msg, to_cont);
5336}
5337
5338fn test_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5339 let is_control = {
5340 let g = state.global();
5341 !g.test_warn_last_to_cont && !to_cont && msg.first() == Some(&b'@')
5342 };
5343 if is_control {
5344 let mut g = state.global_mut();
5345 match &msg[1..] {
5346 b"off" => g.test_warn_on = false,
5347 b"on" => g.test_warn_on = true,
5348 b"normal" => g.test_warn_mode = TestWarnMode::Normal,
5349 b"allow" => g.test_warn_mode = TestWarnMode::Allow,
5350 b"store" => g.test_warn_mode = TestWarnMode::Store,
5351 _ => {}
5352 }
5353 return;
5354 }
5355
5356 let finished = {
5357 let mut g = state.global_mut();
5358 g.test_warn_last_to_cont = to_cont;
5359 g.test_warn_buffer.extend_from_slice(msg);
5360 if to_cont {
5361 None
5362 } else {
5363 Some((
5364 std::mem::take(&mut g.test_warn_buffer),
5365 g.test_warn_mode,
5366 g.test_warn_on,
5367 ))
5368 }
5369 };
5370
5371 let Some((message, mode, warn_on)) = finished else {
5372 return;
5373 };
5374 match mode {
5375 TestWarnMode::Normal => {
5376 if warn_on && message.first() == Some(&b'#') {
5377 write_warning_message(&message);
5378 }
5379 }
5380 TestWarnMode::Allow => {
5381 if warn_on {
5382 write_warning_message(&message);
5383 }
5384 }
5385 TestWarnMode::Store => {
5386 if let Ok(s) = state.intern_str(&message) {
5387 state.push(LuaValue::Str(s));
5388 let _ = crate::api::set_global(state, b"_WARN");
5389 }
5390 }
5391 }
5392}
5393
5394fn write_warning_message(message: &[u8]) {
5395 use std::io::Write;
5396 let stderr = std::io::stderr();
5397 let mut h = stderr.lock();
5398 let _ = h.write_all(b"Lua warning: ");
5399 let _ = h.write_all(message);
5400 let _ = h.write_all(b"\n");
5401}
5402
5403fn default_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5408 use std::io::Write;
5409 if !to_cont && msg.first() == Some(&b'@') {
5411 match &msg[1..] {
5412 b"off" => state.global_mut().warn_mode = WarnMode::Off,
5413 b"on" => state.global_mut().warn_mode = WarnMode::On,
5414 _ => {}
5415 }
5416 return;
5417 }
5418 let mode = state.global().warn_mode;
5419 match mode {
5420 WarnMode::Off => {}
5421 WarnMode::On | WarnMode::Cont => {
5422 let stderr = std::io::stderr();
5423 let mut h = stderr.lock();
5424 if mode == WarnMode::On {
5425 let _ = h.write_all(b"Lua warning: ");
5426 }
5427 let _ = h.write_all(msg);
5428 if to_cont {
5429 state.global_mut().warn_mode = WarnMode::Cont;
5430 } else {
5431 let _ = h.write_all(b"\n");
5432 state.global_mut().warn_mode = WarnMode::On;
5433 }
5434 }
5435 }
5436}
5437
5438#[cfg(test)]
5439mod tests {
5440 use super::*;
5441
5442 fn test_noop_cclosure(_: &mut LuaState) -> Result<usize, LuaError> {
5443 Ok(0)
5444 }
5445
5446 #[test]
5447 fn external_root_keys_reject_stale_slot_after_reuse() {
5448 let mut roots = ExternalRootSet::default();
5449
5450 let first = roots.insert(LuaValue::Int(1));
5451 assert_eq!(roots.len(), 1);
5452 assert_eq!(roots.get(first), Some(&LuaValue::Int(1)));
5453
5454 assert_eq!(roots.remove(first), Some(LuaValue::Int(1)));
5455 assert!(roots.get(first).is_none());
5456 assert!(roots.remove(first).is_none());
5457 assert_eq!(roots.len(), 0);
5458 assert_eq!(roots.vacant_len(), 1);
5459 assert!(roots.replace(first, LuaValue::Int(9)).is_none());
5460 assert!(roots.is_empty());
5461
5462 let second = roots.insert(LuaValue::Int(2));
5463 assert_eq!(first.index, second.index);
5464 assert_ne!(first, second);
5465 assert!(roots.get(first).is_none());
5466 assert_eq!(roots.get(second), Some(&LuaValue::Int(2)));
5467 assert!(roots.replace(first, LuaValue::Int(3)).is_none());
5468 }
5469
5470 #[test]
5471 fn external_roots_keep_heap_value_alive_until_unrooted() {
5472 let mut state = new_state().expect("state should initialize");
5473 let _heap_guard = {
5474 let g = state.global();
5475 lua_gc::HeapGuard::push(&g.heap)
5476 };
5477
5478 let table = state.new_table();
5479 assert_eq!(state.global().heap.allgc_count(), 1);
5480
5481 let key = state.external_root_value(LuaValue::Table(table));
5482 state.gc().full_collect();
5483 assert_eq!(state.global().heap.allgc_count(), 1);
5484 assert_eq!(state.global().external_roots.len(), 1);
5485
5486 assert!(state.external_unroot_value(key).is_some());
5487 state.gc().full_collect();
5488 assert_eq!(state.global().heap.allgc_count(), 0);
5489 assert!(state.global().external_roots.is_empty());
5490 }
5491
5492 #[test]
5493 fn table_buffer_accounting_refunds_on_sweep() {
5494 let mut state = new_state().expect("state should initialize");
5495 let _heap_guard = {
5496 let g = state.global();
5497 lua_gc::HeapGuard::push(&g.heap)
5498 };
5499
5500 let table = state.new_table();
5501 let key = state.external_root_value(LuaValue::Table(table));
5502 let header_bytes = state.global().heap.bytes_used();
5503 assert!(header_bytes > 0);
5504
5505 for i in 1..=128 {
5506 table
5507 .raw_set_int(&mut state, i, LuaValue::Int(i))
5508 .expect("integer table insert should succeed");
5509 }
5510 let grown_bytes = state.global().heap.bytes_used();
5511 assert!(
5512 grown_bytes > header_bytes,
5513 "table array/hash buffer growth must be charged to the GC heap"
5514 );
5515
5516 state.gc().full_collect();
5517 assert_eq!(
5518 state.global().heap.bytes_used(),
5519 grown_bytes,
5520 "rooted table buffer bytes should remain charged after collection"
5521 );
5522
5523 assert!(state.external_unroot_value(key).is_some());
5524 state.gc().full_collect();
5525 assert_eq!(state.global().heap.bytes_used(), 0);
5526 assert_eq!(state.global().heap.allgc_count(), 0);
5527 }
5528
5529 #[test]
5530 fn userdata_buffer_accounting_refunds_on_sweep() {
5531 let mut state = new_state().expect("state should initialize");
5532 let _heap_guard = {
5533 let g = state.global();
5534 lua_gc::HeapGuard::push(&g.heap)
5535 };
5536
5537 let payload_len = 4096;
5538 let userdata = state
5539 .new_userdata_typed(b"accounting", payload_len, 3)
5540 .expect("userdata allocation should succeed");
5541 state.pop_n(1);
5542 let key = state.external_root_value(LuaValue::UserData(userdata));
5543 let allocated_bytes = state.global().heap.bytes_used();
5544 assert!(
5545 allocated_bytes > payload_len,
5546 "userdata payload bytes must be charged to the GC heap"
5547 );
5548
5549 state.gc().full_collect();
5550 assert_eq!(
5551 state.global().heap.bytes_used(),
5552 allocated_bytes,
5553 "rooted userdata payload bytes should remain charged after collection"
5554 );
5555
5556 assert!(state.external_unroot_value(key).is_some());
5557 state.gc().full_collect();
5558 assert_eq!(state.global().heap.bytes_used(), 0);
5559 assert_eq!(state.global().heap.allgc_count(), 0);
5560 }
5561
5562 #[test]
5563 fn cclosure_upvalue_accounting_refunds_on_sweep() {
5564 let mut state = new_state().expect("state should initialize");
5565 let _heap_guard = {
5566 let g = state.global();
5567 lua_gc::HeapGuard::push(&g.heap)
5568 };
5569
5570 let nupvalues = 64;
5571 for i in 0..nupvalues {
5572 state.push(LuaValue::Int(i as i64));
5573 }
5574 crate::api::push_cclosure(&mut state, test_noop_cclosure, nupvalues as i32)
5575 .expect("C closure creation should succeed");
5576 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5577 panic!("expected heavy C closure");
5578 };
5579 let expected_payload = ccl.buffer_bytes();
5580 let key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
5581 state.pop_n(1);
5582 let allocated_bytes = state.global().heap.bytes_used();
5583 assert!(
5584 allocated_bytes >= expected_payload,
5585 "C closure upvalue vector bytes must be charged to the GC heap"
5586 );
5587
5588 state.gc().full_collect();
5589 assert_eq!(
5590 state.global().heap.bytes_used(),
5591 allocated_bytes,
5592 "rooted C closure payload bytes should remain charged after collection"
5593 );
5594
5595 assert!(state.external_unroot_value(key).is_some());
5596 state.gc().full_collect();
5597 assert_eq!(state.global().heap.bytes_used(), 0);
5598 assert_eq!(state.global().heap.allgc_count(), 0);
5599 }
5600
5601 #[test]
5602 fn proto_and_lclosure_accounting_refunds_on_sweep() {
5603 let mut state = new_state().expect("state should initialize");
5604 let _heap_guard = {
5605 let g = state.global();
5606 lua_gc::HeapGuard::push(&g.heap)
5607 };
5608
5609 let mut proto = LuaProto::placeholder();
5610 proto.code = vec![lua_types::opcode::Instruction(0); 2048];
5611 proto.lineinfo = vec![0; 2048];
5612 proto.k = vec![LuaValue::Int(1); 512];
5613 let expected_proto_payload = proto.buffer_bytes();
5614 let proto = GcRef::new(proto);
5615 proto.account_buffer(expected_proto_payload as isize);
5616
5617 let closure = state.new_lclosure(proto, 16);
5618 let expected_closure_payload = closure.buffer_bytes();
5619 let key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5620 let allocated_bytes = state.global().heap.bytes_used();
5621 assert!(
5622 allocated_bytes >= expected_proto_payload + expected_closure_payload,
5623 "proto and Lua closure vector bytes must be charged to the GC heap"
5624 );
5625
5626 state.gc().full_collect();
5627 assert_eq!(
5628 state.global().heap.bytes_used(),
5629 allocated_bytes,
5630 "rooted proto and Lua closure payload bytes should remain charged after collection"
5631 );
5632
5633 assert!(state.external_unroot_value(key).is_some());
5634 state.gc().full_collect();
5635 assert_eq!(state.global().heap.bytes_used(), 0);
5636 assert_eq!(state.global().heap.allgc_count(), 0);
5637 }
5638
5639 #[test]
5640 fn string_buffer_accounting_refunds_on_sweep() {
5641 let mut state = new_state().expect("state should initialize");
5642 let _heap_guard = {
5643 let g = state.global();
5644 lua_gc::HeapGuard::push(&g.heap)
5645 };
5646
5647 let payload = vec![b'x'; crate::string::MAX_SHORT_LEN + 4096];
5648 let string = state.intern_str(&payload).expect("long string should allocate");
5649 let key = state.external_root_value(LuaValue::Str(string));
5650 let allocated_bytes = state.global().heap.bytes_used();
5651 assert!(
5652 allocated_bytes > payload.len(),
5653 "long string backing bytes must be charged to the GC heap"
5654 );
5655
5656 state.gc().full_collect();
5657 assert_eq!(
5658 state.global().heap.bytes_used(),
5659 allocated_bytes,
5660 "rooted string buffer bytes should remain charged after collection"
5661 );
5662
5663 assert!(state.external_unroot_value(key).is_some());
5664 state.gc().full_collect();
5665 assert_eq!(state.global().heap.bytes_used(), 0);
5666 assert_eq!(state.global().heap.allgc_count(), 0);
5667 }
5668
5669 #[test]
5670 fn interned_short_string_cache_does_not_root_unreferenced_string() {
5671 let mut state = new_state().expect("state should initialize");
5672 let _heap_guard = {
5673 let g = state.global();
5674 lua_gc::HeapGuard::push(&g.heap)
5675 };
5676
5677 let payload = b"weak-cache-probe-a";
5678 let string = state
5679 .intern_str(payload)
5680 .expect("short string should intern");
5681 let id = string.identity();
5682 assert!(state.global().interned_lt.contains_key(&payload[..]));
5683 assert!(state.global().heap.allocation_token(id).is_some());
5684
5685 state.gc().full_collect();
5686 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5687 assert_eq!(state.global().heap.allocation_token(id), None);
5688 }
5689
5690 #[test]
5691 fn interned_short_string_cache_keeps_reachable_string_until_unrooted() {
5692 let mut state = new_state().expect("state should initialize");
5693 let _heap_guard = {
5694 let g = state.global();
5695 lua_gc::HeapGuard::push(&g.heap)
5696 };
5697
5698 let payload = b"weak-cache-probe-b";
5699 let string = state
5700 .intern_str(payload)
5701 .expect("short string should intern");
5702 let id = string.identity();
5703 let key = state.external_root_value(LuaValue::Str(string));
5704
5705 state.gc().full_collect();
5706 assert!(state.global().interned_lt.contains_key(&payload[..]));
5707 assert!(state.global().heap.allocation_token(id).is_some());
5708
5709 assert!(state.external_unroot_value(key).is_some());
5710 state.gc().full_collect();
5711 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5712 assert_eq!(state.global().heap.allocation_token(id), None);
5713 }
5714
5715 #[test]
5716 fn gc_phase_predicates_follow_heap_state() {
5717 let mut state = new_state().expect("state should initialize");
5718 let _heap_guard = {
5719 let g = state.global();
5720 lua_gc::HeapGuard::push(&g.heap)
5721 };
5722
5723 {
5724 let mut g = state.global_mut();
5725 g.gckind = GcKind::Incremental as u8;
5726 g.lastatomic = 0;
5727 assert!(!g.is_gen_mode());
5728 g.lastatomic = 1;
5729 assert!(g.is_gen_mode());
5730 g.lastatomic = 0;
5731 }
5732
5733 let mut roots = Vec::new();
5734 for _ in 0..16 {
5735 let table = state.new_table();
5736 roots.push(state.external_root_value(LuaValue::Table(table)));
5737 }
5738
5739 let mut saw_keep = false;
5740 let mut saw_sweep = false;
5741 for _ in 0..128 {
5742 state.gc().incremental_step(1);
5743 let g = state.global();
5744 let heap_state = g.heap.gc_state();
5745 assert_eq!(
5746 g.keep_invariant(),
5747 heap_state.is_invariant()
5748 );
5749 assert_eq!(g.is_sweep_phase(), heap_state.is_sweep());
5750 saw_keep |= g.keep_invariant();
5751 saw_sweep |= g.is_sweep_phase();
5752 if heap_state.is_pause() && saw_keep && saw_sweep {
5753 break;
5754 }
5755 }
5756
5757 assert!(saw_keep, "incremental cycle should expose an invariant phase");
5758 assert!(saw_sweep, "incremental cycle should expose a sweep phase");
5759
5760 for key in roots {
5761 assert!(state.external_unroot_value(key).is_some());
5762 }
5763 state.gc().full_collect();
5764 }
5765
5766 #[test]
5767 fn gc_barrier_keeps_new_child_stored_in_black_parent() {
5768 let mut state = new_state().expect("state should initialize");
5769 let _heap_guard = {
5770 let g = state.global();
5771 lua_gc::HeapGuard::push(&g.heap)
5772 };
5773
5774 let parent = state.new_table();
5775 let parent_key = state.external_root_value(LuaValue::Table(parent));
5776 state.gc().incremental_step(1);
5777 assert!(
5778 state.global().keep_invariant(),
5779 "test setup should leave the parent marked during an active cycle"
5780 );
5781
5782 let child = state.new_table();
5783 let parent_value = LuaValue::Table(parent);
5784 let child_value = LuaValue::Table(child);
5785 parent
5786 .raw_set_int(&mut state, 1, child_value)
5787 .expect("table store should succeed");
5788 state.gc_barrier_back(&parent_value, &child_value);
5789
5790 for _ in 0..128 {
5791 if state.gc().incremental_step(1) {
5792 break;
5793 }
5794 }
5795
5796 assert_eq!(state.global().heap.allgc_count(), 2);
5797 assert_eq!(
5798 parent.get_int(1).as_table().map(|t| t.identity()),
5799 Some(child.identity())
5800 );
5801
5802 assert!(state.external_unroot_value(parent_key).is_some());
5803 state.gc().full_collect();
5804 assert_eq!(state.global().heap.allgc_count(), 0);
5805 }
5806
5807 #[test]
5808 fn generational_mode_promotes_and_barriers_age_objects() {
5809 let mut state = new_state().expect("state should initialize");
5810 let _heap_guard = {
5811 let g = state.global();
5812 lua_gc::HeapGuard::push(&g.heap)
5813 };
5814
5815 let parent = state.new_table();
5816 let parent_key = state.external_root_value(LuaValue::Table(parent));
5817
5818 state.gc().change_mode(GcKind::Generational);
5819 assert_eq!(parent.0.age(), lua_gc::GcAge::Old);
5820 assert_eq!(parent.0.color(), lua_gc::Color::Black);
5821 let majorbase = state.global().gc_estimate;
5822 assert!(majorbase > 0);
5823 assert!(state.global().gc_debt() <= 0);
5824
5825 let child = state.new_table();
5826 let parent_value = LuaValue::Table(parent);
5827 let child_value = LuaValue::Table(child);
5828 parent
5829 .raw_set_int(&mut state, 1, child_value.clone())
5830 .expect("table store should succeed");
5831 state.gc_barrier_back(&parent_value, &child_value);
5832 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched1);
5833 assert_eq!(parent.0.color(), lua_gc::Color::Gray);
5834 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5835
5836 let metatable = state.new_table();
5837 parent.set_metatable(Some(metatable));
5838 state.gc().obj_barrier(&parent, &metatable);
5839 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old0);
5840
5841 assert!(state.gc().generational_step_minor_only());
5842 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched2);
5843 assert_eq!(child.0.age(), lua_gc::GcAge::Survival);
5844 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old1);
5845 assert_eq!(state.global().gc_estimate, majorbase);
5846 assert!(state.global().gc_debt() <= 0);
5847
5848 state.gc().change_mode(GcKind::Incremental);
5849 assert_eq!(parent.0.age(), lua_gc::GcAge::New);
5850 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5851 assert_eq!(metatable.0.age(), lua_gc::GcAge::New);
5852
5853 assert!(state.external_unroot_value(parent_key).is_some());
5854 state.gc().full_collect();
5855 }
5856
5857 #[test]
5858 fn generational_upvalue_write_barrier_marks_young_child_old0() {
5859 let mut state = new_state().expect("state should initialize");
5860 let _heap_guard = {
5861 let g = state.global();
5862 lua_gc::HeapGuard::push(&g.heap)
5863 };
5864
5865 let proto = state.new_proto();
5866 let closure = state.new_lclosure(proto, 1);
5867 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5868 state.gc().change_mode(GcKind::Generational);
5869 let uv = closure.upval(0);
5870 assert_eq!(uv.0.age(), lua_gc::GcAge::Old);
5871
5872 let child = state.new_table();
5873 state
5874 .upvalue_set(&closure, 0, LuaValue::Table(child))
5875 .expect("closed upvalue write should succeed");
5876 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
5877
5878 assert!(state.external_unroot_value(closure_key).is_some());
5879 state.gc().full_collect();
5880 }
5881
5882 #[test]
5883 fn cclosure_setupvalue_replaces_upvalue() {
5884 let mut state = new_state().expect("state should initialize");
5885 let _heap_guard = {
5886 let g = state.global();
5887 lua_gc::HeapGuard::push(&g.heap)
5888 };
5889
5890 let first = state.new_table();
5891 state.push(LuaValue::Table(first));
5892 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
5893 .expect("C closure creation should succeed");
5894 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5895 panic!("expected heavy C closure");
5896 };
5897
5898 let second = state.new_table();
5899 state.push(LuaValue::Table(second));
5900 let name = crate::api::setup_value(&mut state, -2, 1)
5901 .expect("C closure upvalue should exist");
5902
5903 assert!(name.is_empty());
5904 let upvalues = ccl.upvalues.borrow();
5905 let LuaValue::Table(actual) = upvalues[0].clone() else {
5906 panic!("expected table upvalue");
5907 };
5908 assert_eq!(actual.identity(), second.identity());
5909 }
5910
5911 #[test]
5912 fn generational_cclosure_setupvalue_barrier_marks_young_child_old0() {
5913 let mut state = new_state().expect("state should initialize");
5914 let _heap_guard = {
5915 let g = state.global();
5916 lua_gc::HeapGuard::push(&g.heap)
5917 };
5918
5919 state.push(LuaValue::Nil);
5920 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
5921 .expect("C closure creation should succeed");
5922 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5923 panic!("expected heavy C closure");
5924 };
5925 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
5926
5927 state.gc().change_mode(GcKind::Generational);
5928 assert_eq!(ccl.0.age(), lua_gc::GcAge::Old);
5929
5930 let child = state.new_table();
5931 state.push(LuaValue::Table(child));
5932 crate::api::setup_value(&mut state, -2, 1)
5933 .expect("C closure upvalue should exist");
5934
5935 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
5936
5937 assert!(state.external_unroot_value(closure_key).is_some());
5938 state.gc().full_collect();
5939 }
5940
5941 #[test]
5942 fn generational_closure_upvalue_slot_barrier_marks_new_upval_old0() {
5943 let mut state = new_state().expect("state should initialize");
5944 let _heap_guard = {
5945 let g = state.global();
5946 lua_gc::HeapGuard::push(&g.heap)
5947 };
5948
5949 let proto = state.new_proto();
5950 let closure = state.new_lclosure(proto, 1);
5951 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5952 state.gc().change_mode(GcKind::Generational);
5953 assert_eq!(closure.0.age(), lua_gc::GcAge::Old);
5954
5955 let replacement = state.new_upval_closed(LuaValue::Nil);
5956 closure.set_upval(0, replacement);
5957 state.gc().obj_barrier(&closure, &replacement);
5958 assert_eq!(replacement.0.age(), lua_gc::GcAge::Old0);
5959
5960 assert!(state.external_unroot_value(closure_key).is_some());
5961 state.gc().full_collect();
5962 }
5963
5964 #[test]
5965 fn cross_thread_upvalue_mirror_traces_values_as_roots() {
5966 let mut state = new_state().expect("state should initialize");
5967 let _heap_guard = {
5968 let g = state.global();
5969 lua_gc::HeapGuard::push(&g.heap)
5970 };
5971
5972 let mirrored = state.new_table();
5973 state
5974 .global_mut()
5975 .cross_thread_upvals
5976 .insert((999, StackIdx(0)), LuaValue::Table(mirrored));
5977
5978 state.gc().full_collect();
5979 assert_eq!(state.global().heap.allgc_count(), 1);
5980
5981 state.global_mut().cross_thread_upvals.clear();
5982 state.gc().full_collect();
5983 assert_eq!(state.global().heap.allgc_count(), 0);
5984 }
5985
5986 #[test]
5987 fn generational_full_collect_promotes_new_survivors_to_old() {
5988 let mut state = new_state().expect("state should initialize");
5989 let _heap_guard = {
5990 let g = state.global();
5991 lua_gc::HeapGuard::push(&g.heap)
5992 };
5993
5994 state.gc().change_mode(GcKind::Generational);
5995 let table = state.new_table();
5996 let table_key = state.external_root_value(LuaValue::Table(table));
5997 assert_eq!(table.0.age(), lua_gc::GcAge::New);
5998
5999 state.gc().full_collect();
6000 assert_eq!(table.0.age(), lua_gc::GcAge::Old);
6001 assert_eq!(table.0.color(), lua_gc::Color::Black);
6002
6003 assert!(state.external_unroot_value(table_key).is_some());
6004 state.gc().full_collect();
6005 }
6006
6007 #[test]
6008 fn gc_packed_params_return_user_visible_values() {
6009 let mut state = new_state().expect("state should initialize");
6010 assert_eq!(
6011 crate::api::gc(&mut state, crate::api::GcArgs::SetPause { value: 200 }),
6012 200
6013 );
6014 assert_eq!(state.global().gc_pause_param(), 200);
6015 assert_eq!(
6016 crate::api::gc(&mut state, crate::api::GcArgs::SetStepMul { value: 200 }),
6017 100
6018 );
6019 assert_eq!(state.global().gc_stepmul_param(), 200);
6020
6021 crate::api::gc(
6022 &mut state,
6023 crate::api::GcArgs::Gen {
6024 minormul: 0,
6025 majormul: 200,
6026 },
6027 );
6028 assert_eq!(state.global().gc_genmajormul_param(), 200);
6029 }
6030
6031 #[test]
6032 fn generational_step_runs_bad_major_when_growth_exceeds_genmajormul() {
6033 let mut state = new_state().expect("state should initialize");
6034 let _heap_guard = {
6035 let g = state.global();
6036 lua_gc::HeapGuard::push(&g.heap)
6037 };
6038
6039 let root = state.new_table();
6040 let root_key = state.external_root_value(LuaValue::Table(root));
6041 state.gc().change_mode(GcKind::Generational);
6042
6043 let root_value = LuaValue::Table(root);
6044 for i in 1..=64 {
6045 let child = state.new_table();
6046 let child_value = LuaValue::Table(child);
6047 root
6048 .raw_set_int(&mut state, i, child_value.clone())
6049 .expect("table store should succeed");
6050 state.gc_barrier_back(&root_value, &child_value);
6051 }
6052
6053 {
6054 let mut g = state.global_mut();
6055 g.gc_estimate = 1;
6056 set_debt(&mut *g, 1);
6057 }
6058
6059 assert!(state.gc().generational_step());
6060 let g = state.global();
6061 assert!(g.is_gen_mode());
6062 assert!(g.lastatomic > 0, "bad major collection should arm stepgenfull");
6063 assert!(g.gc_estimate > 1);
6064 assert!(g.gc_debt() <= 0);
6065 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6066 drop(g);
6067
6068 assert!(state.external_unroot_value(root_key).is_some());
6069 state.gc().full_collect();
6070 }
6071
6072 #[test]
6073 fn generational_implicit_step_runs_major_when_heap_threshold_exceeded() {
6074 let mut state = new_state().expect("state should initialize");
6075 let _heap_guard = {
6076 let g = state.global();
6077 lua_gc::HeapGuard::push(&g.heap)
6078 };
6079
6080 let root = state.new_table();
6081 let root_key = state.external_root_value(LuaValue::Table(root));
6082 state.gc().change_mode(GcKind::Generational);
6083
6084 let root_value = LuaValue::Table(root);
6085 for i in 1..=64 {
6086 let child = state.new_table();
6087 let child_value = LuaValue::Table(child);
6088 root
6089 .raw_set_int(&mut state, i, child_value.clone())
6090 .expect("table store should succeed");
6091 state.gc_barrier_back(&root_value, &child_value);
6092 }
6093
6094 {
6095 let mut g = state.global_mut();
6096 g.gc_estimate = 1;
6097 set_debt(&mut *g, -1);
6098 g.heap.set_threshold_bytes(1);
6099 }
6100
6101 assert!(state.gc().generational_step());
6102 let g = state.global();
6103 assert!(g.is_gen_mode());
6104 assert!(
6105 g.lastatomic > 0,
6106 "implicit threshold-triggered growth should arm a bad major"
6107 );
6108 assert!(g.gc_debt() <= 0);
6109 drop(g);
6110
6111 assert!(state.external_unroot_value(root_key).is_some());
6112 state.gc().full_collect();
6113 }
6114
6115 #[test]
6116 fn generational_stepgenfull_returns_to_gen_after_good_collection() {
6117 let mut state = new_state().expect("state should initialize");
6118 let _heap_guard = {
6119 let g = state.global();
6120 lua_gc::HeapGuard::push(&g.heap)
6121 };
6122
6123 let root = state.new_table();
6124 let root_key = state.external_root_value(LuaValue::Table(root));
6125 state.gc().change_mode(GcKind::Generational);
6126 {
6127 let mut g = state.global_mut();
6128 g.lastatomic = 1024;
6129 }
6130
6131 assert!(state.gc().generational_step());
6132 let g = state.global();
6133 assert_eq!(g.gckind, GcKind::Generational as u8);
6134 assert_eq!(g.lastatomic, 0);
6135 assert!(g.gc_debt() <= 0);
6136 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6137 assert_eq!(root.0.color(), lua_gc::Color::Black);
6138 drop(g);
6139
6140 assert!(state.external_unroot_value(root_key).is_some());
6141 state.gc().full_collect();
6142 }
6143
6144 #[test]
6145 fn generational_step_zero_reports_false_without_positive_debt() {
6146 let mut state = new_state().expect("state should initialize");
6147 let _heap_guard = {
6148 let g = state.global();
6149 lua_gc::HeapGuard::push(&g.heap)
6150 };
6151
6152 state.gc().change_mode(GcKind::Generational);
6153 assert_eq!(
6154 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 0 }),
6155 0
6156 );
6157 assert_eq!(
6158 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 1 }),
6159 1
6160 );
6161 }
6162}
6163
6164