1use std::cell::RefCell;
23use std::collections::hash_map::Entry;
24use std::collections::HashMap;
25use std::hash::{BuildHasherDefault, Hasher};
26use std::rc::Rc;
27
28use crate::string::StringPool;
29pub use lua_types::error::LuaError;
30pub use lua_types::{CallInfoIdx, StackIdx};
31
32pub struct StackIdxConv(pub StackIdx);
35
36#[inline(always)]
41pub fn stack_idx_to_i32(i: StackIdx) -> i32 { i.0 as i32 }
42
43impl From<u32> for StackIdxConv {
44 #[inline(always)]
45 fn from(v: u32) -> Self { StackIdxConv(StackIdx(v)) }
46}
47impl From<i32> for StackIdxConv {
48 #[inline(always)]
49 fn from(v: i32) -> Self { StackIdxConv(StackIdx(v.max(0) as u32)) }
50}
51impl From<usize> for StackIdxConv {
52 #[inline(always)]
53 fn from(v: usize) -> Self { StackIdxConv(StackIdx(v as u32)) }
54}
55impl From<StackIdx> for StackIdxConv {
56 #[inline(always)]
57 fn from(v: StackIdx) -> Self { StackIdxConv(v) }
58}
59pub use lua_types::value::{LuaTable, LuaValue, F2Imod};
60pub use lua_types::string::LuaString;
61pub use lua_types::userdata::LuaUserData;
62pub use lua_types::closure::{LuaCFnPtr, LuaClosure, LuaLClosure as LuaClosureLua, LuaCClosure as LuaClosureC};
63pub use lua_types::proto::LuaProto;
64pub use lua_types::upval::{UpVal, UpValState};
65pub use lua_types::gc::GcRef;
66
67pub struct LuaByteHasher {
68 hash: u64,
69}
70
71impl Default for LuaByteHasher {
72 fn default() -> Self {
73 Self { hash: 0xcbf2_9ce4_8422_2325 }
74 }
75}
76
77impl Hasher for LuaByteHasher {
78 #[inline]
79 fn write(&mut self, bytes: &[u8]) {
80 const PRIME: u64 = 0x0000_0100_0000_01b3;
81 for &byte in bytes {
82 self.hash ^= u64::from(byte);
83 self.hash = self.hash.wrapping_mul(PRIME);
84 }
85 }
86
87 #[inline]
88 fn write_u8(&mut self, i: u8) {
89 self.write(&[i]);
90 }
91
92 #[inline]
93 fn write_usize(&mut self, i: usize) {
94 self.write(&i.to_ne_bytes());
95 }
96
97 #[inline]
98 fn finish(&self) -> u64 {
99 self.hash
100 }
101}
102
103pub type LuaByteBuildHasher = BuildHasherDefault<LuaByteHasher>;
104pub type InternedStringMap = HashMap<Box<[u8]>, GcRef<LuaString>, LuaByteBuildHasher>;
105
106pub type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
113
114pub type LuaRustFunction = Rc<dyn Fn(&mut LuaState) -> Result<usize, LuaError>>;
115
116#[derive(Clone)]
117pub enum LuaCallable {
118 Bare(LuaCFunction),
119 Rust(LuaRustFunction),
120}
121
122impl std::fmt::Debug for LuaCallable {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 LuaCallable::Bare(_) => f.write_str("LuaCallable::Bare(..)"),
126 LuaCallable::Rust(_) => f.write_str("LuaCallable::Rust(..)"),
127 }
128 }
129}
130
131impl LuaCallable {
132 pub fn bare(f: LuaCFunction) -> Self {
133 LuaCallable::Bare(f)
134 }
135
136 pub fn rust(f: LuaRustFunction) -> Self {
137 LuaCallable::Rust(f)
138 }
139
140 pub fn as_bare(&self) -> Option<LuaCFunction> {
141 match self {
142 LuaCallable::Bare(f) => Some(*f),
143 LuaCallable::Rust(_) => None,
144 }
145 }
146
147 pub fn call(&self, state: &mut LuaState) -> Result<usize, LuaError> {
148 match self {
149 LuaCallable::Bare(f) => f(state),
150 LuaCallable::Rust(f) => f(state),
151 }
152 }
153}
154
155#[derive(Clone, Debug)]
156pub enum FinalizerObject {
157 Table(GcRef<LuaTable>),
158 UserData(GcRef<LuaUserData>),
159}
160
161impl FinalizerObject {
162 pub fn identity(&self) -> usize {
163 match self {
164 FinalizerObject::Table(t) => t.identity(),
165 FinalizerObject::UserData(u) => u.identity(),
166 }
167 }
168
169 pub fn metatable(&self) -> Option<GcRef<LuaTable>> {
170 match self {
171 FinalizerObject::Table(t) => t.metatable(),
172 FinalizerObject::UserData(u) => u.metatable(),
173 }
174 }
175
176 pub fn as_lua_value(&self) -> LuaValue {
177 match self {
178 FinalizerObject::Table(t) => LuaValue::Table(t.clone()),
179 FinalizerObject::UserData(u) => LuaValue::UserData(u.clone()),
180 }
181 }
182
183 pub fn mark(&self, marker: &mut lua_gc::Marker) {
184 match self {
185 FinalizerObject::Table(t) => marker.mark(t.0),
186 FinalizerObject::UserData(u) => marker.mark(u.0),
187 }
188 }
189
190 pub fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
191 Some(match self {
192 FinalizerObject::Table(t) => t.0.as_trace_ptr(),
193 FinalizerObject::UserData(u) => u.0.as_trace_ptr(),
194 })
195 }
196
197 pub fn age(&self) -> lua_gc::GcAge {
198 match self {
199 FinalizerObject::Table(t) => t.0.age(),
200 FinalizerObject::UserData(u) => u.0.age(),
201 }
202 }
203
204 pub fn is_finalized(&self) -> bool {
205 match self {
206 FinalizerObject::Table(t) => t.0.is_finalized(),
207 FinalizerObject::UserData(u) => u.0.is_finalized(),
208 }
209 }
210
211 pub fn set_finalized(&self, finalized: bool) {
212 match self {
213 FinalizerObject::Table(t) => t.0.set_finalized(finalized),
214 FinalizerObject::UserData(u) => u.0.set_finalized(finalized),
215 }
216 }
217}
218
219impl lua_gc::FinalizerEntry for FinalizerObject {
220 fn identity(&self) -> usize {
221 FinalizerObject::identity(self)
222 }
223
224 fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
225 FinalizerObject::heap_ptr(self)
226 }
227
228 fn age(&self) -> lua_gc::GcAge {
229 FinalizerObject::age(self)
230 }
231
232 fn is_finalized(&self) -> bool {
233 FinalizerObject::is_finalized(self)
234 }
235
236 fn set_finalized(&self, finalized: bool) {
237 FinalizerObject::set_finalized(self, finalized);
238 }
239}
240
241#[derive(Clone, Debug)]
242pub struct WeakTableEntry {
243 table: lua_types::gc::GcWeak<LuaTable>,
244 kind: lua_gc::WeakListKind,
245}
246
247impl WeakTableEntry {
248 pub fn new(table: &GcRef<LuaTable>) -> Self {
249 let mode = table.weak_mode();
250 let weak_keys = (mode & (1 << 0)) != 0;
251 let weak_values = (mode & (1 << 1)) != 0;
252 let kind = match (weak_keys, weak_values) {
253 (true, true) => lua_gc::WeakListKind::AllWeak,
254 (true, false) => lua_gc::WeakListKind::Ephemeron,
255 (false, true) => lua_gc::WeakListKind::WeakValues,
256 (false, false) => lua_gc::WeakListKind::WeakValues,
257 };
258 Self { table: table.downgrade(), kind }
259 }
260}
261
262impl lua_gc::WeakEntry for WeakTableEntry {
263 type Strong = GcRef<LuaTable>;
264
265 fn identity(&self) -> usize {
266 self.table.identity()
267 }
268
269 fn list_kind(&self) -> lua_gc::WeakListKind {
270 self.kind
271 }
272
273 fn upgrade(&self) -> Option<Self::Strong> {
274 self.table.upgrade()
275 }
276}
277
278pub(crate) const EXTRA_STACK: usize = 5;
282
283pub(crate) const LUA_MINSTACK: usize = 20;
285
286pub(crate) const BASIC_STACK_SIZE: usize = 2 * LUA_MINSTACK;
288
289pub(crate) const LUAI_MAXCCALLS: u32 = 200;
309
310pub(crate) const CIST_C: u16 = 1 << 1;
312
313pub(crate) const CIST_OAH: u16 = 1 << 0;
315pub(crate) const CIST_FRESH: u16 = 1 << 2;
316pub(crate) const CIST_HOOKED: u16 = 1 << 3;
317pub(crate) const CIST_YPCALL: u16 = 1 << 4;
318pub(crate) const CIST_TAIL: u16 = 1 << 5;
319pub(crate) const CIST_HOOKYIELD: u16 = 1 << 6;
320pub(crate) const CIST_FIN: u16 = 1 << 7;
321pub(crate) const CIST_TRAN: u16 = 1 << 8;
322pub(crate) const CIST_RECST: u32 = 10;
323pub(crate) const CIST_LEQ: u16 = 1 << 13;
330
331const LUA_NUMTYPES: usize = 9;
333
334const GCSTPUSR: u8 = 1;
336const GCSTPGC: u8 = 2;
337
338const GCS_PAUSE: u8 = 0;
340
341const LUAI_GCPAUSE: u32 = 200;
342const LUAI_GCMUL: u32 = 100;
343const LUAI_GCSTEPSIZE: u8 = 13;
344const LUAI_GENMAJORMUL: u32 = 100;
345const LUAI_GENMINORMUL: u8 = 20;
346
347const WHITE0BIT: u8 = 0;
348
349const STRCACHE_N: usize = 53;
350const STRCACHE_M: usize = 2;
351
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358pub enum GcKind {
359 Incremental = 0,
360 Generational = 1,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub enum WarnMode {
372 Off,
373 On,
374 Cont,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub enum TestWarnMode {
380 Normal,
381 Allow,
382 Store,
383}
384
385pub use lua_types::status::LuaStatus;
390
391#[derive(Clone)]
398pub struct StackValue {
399 pub val: LuaValue,
400 pub tbc_delta: u16,
401}
402
403impl Default for StackValue {
404 fn default() -> Self {
405 StackValue {
406 val: LuaValue::Nil,
407 tbc_delta: 0,
408 }
409 }
410}
411
412#[derive(Clone)]
421pub struct CallInfo {
422 pub func: StackIdx,
424
425 pub top: StackIdx,
427
428 pub previous: Option<CallInfoIdx>,
430
431 pub next: Option<CallInfoIdx>,
433
434 pub u: CallInfoFrame,
435
436 pub u2: CallInfoExtra,
437
438 pub nresults: i16,
440
441 pub callstatus: u16,
443}
444
445#[derive(Clone, Copy)]
448pub enum CallInfoFrame {
449 Lua {
450 savedpc: u32,
452 trap: bool,
454 nextraargs: i32,
456 },
457 C {
458 k: Option<LuaKFunction>,
460 old_errfunc: isize,
462 ctx: isize,
464 },
465}
466
467pub type LuaKFunction = fn(&mut LuaState, status: i32, ctx: isize) -> Result<usize, LuaError>;
469
470#[derive(Default, Clone, Copy)]
474pub struct CallInfoExtra {
475 pub value: i32,
476 pub ftransfer: u16,
477 pub ntransfer: u16,
478}
479
480impl CallInfoFrame {
481 pub fn c_default() -> Self {
483 CallInfoFrame::C {
484 k: None,
485 old_errfunc: 0,
486 ctx: 0,
487 }
488 }
489
490 pub fn lua_default() -> Self {
492 CallInfoFrame::Lua {
493 savedpc: 0,
494 trap: false,
495 nextraargs: 0,
496 }
497 }
498}
499
500impl Default for CallInfo {
501 fn default() -> Self {
502 CallInfo {
503 func: StackIdx(0),
504 top: StackIdx(0),
505 previous: None,
506 next: None,
507 u: CallInfoFrame::c_default(),
508 u2: CallInfoExtra::default(),
509 nresults: 0,
510 callstatus: 0,
511 }
512 }
513}
514
515impl CallInfo {
516 pub fn is_lua(&self) -> bool { (self.callstatus & CIST_C) == 0 }
517 pub fn is_lua_code(&self) -> bool { self.is_lua() }
518 pub fn is_vararg_func(&self) -> bool { false }
525 pub fn saved_pc(&self) -> u32 {
526 if let CallInfoFrame::Lua { savedpc, .. } = self.u { savedpc } else { 0 }
527 }
528 pub fn set_saved_pc(&mut self, pc: u32) {
529 if let CallInfoFrame::Lua { ref mut savedpc, .. } = self.u { *savedpc = pc; }
530 }
531 pub fn nextra_args(&self) -> i32 {
532 if let CallInfoFrame::Lua { nextraargs, .. } = self.u { nextraargs } else { 0 }
533 }
534 pub fn transfer_ftransfer(&self) -> u16 { self.u2.ftransfer }
535 pub fn transfer_ntransfer(&self) -> u16 { self.u2.ntransfer }
536 pub fn set_trap(&mut self, t: bool) {
537 if let CallInfoFrame::Lua { ref mut trap, .. } = self.u { *trap = t; }
538 }
539 pub fn recover_status(&self) -> i32 {
542 ((self.callstatus >> CIST_RECST) & 7) as i32
543 }
544 pub fn set_recover_status<T: Into<i32>>(&mut self, status: T) {
547 let st = (status.into() & 7) as u16;
548 self.callstatus = (self.callstatus & !(7u16 << CIST_RECST)) | (st << CIST_RECST);
549 }
550 pub fn get_oah(&self) -> bool { (self.callstatus & CIST_OAH) != 0 }
551 pub fn set_oah(&mut self, allow: bool) {
554 self.callstatus = (self.callstatus & !CIST_OAH) | (if allow { CIST_OAH } else { 0 });
555 }
556 pub fn u_c_old_errfunc(&self) -> isize {
557 if let CallInfoFrame::C { old_errfunc, .. } = self.u { old_errfunc } else { 0 }
558 }
559 pub fn u_c_ctx(&self) -> isize {
560 if let CallInfoFrame::C { ctx, .. } = self.u { ctx } else { 0 }
561 }
562 pub fn u_c_k(&self) -> Option<LuaKFunction> {
563 if let CallInfoFrame::C { k, .. } = self.u { k } else { None }
564 }
565 pub fn set_u_c_k(&mut self, k: Option<LuaKFunction>) {
569 if let CallInfoFrame::C { k: ref mut slot, .. } = self.u {
570 *slot = k;
571 }
572 }
573 pub fn set_u_c_ctx(&mut self, ctx: isize) {
575 if let CallInfoFrame::C { ctx: ref mut slot, .. } = self.u {
576 *slot = ctx;
577 }
578 }
579 pub fn set_u_c_old_errfunc(&mut self, old_errfunc: isize) {
581 if let CallInfoFrame::C { old_errfunc: ref mut slot, .. } = self.u {
582 *slot = old_errfunc;
583 }
584 }
585 pub fn set_u2_funcidx(&mut self, idx: i32) {
588 self.u2.value = idx;
589 }
590}
591
592pub trait LuaValueExt {
598 fn base_type(&self) -> lua_types::LuaType;
599 fn to_number_no_strconv(&self) -> Option<f64>;
600 fn to_number_with_strconv(&self) -> Option<f64>;
601 fn to_integer_no_strconv(&self) -> Option<i64>;
602 fn to_integer_with_strconv(&self) -> Option<i64>;
603 fn full_type_tag(&self) -> u8;
604}
605
606impl LuaValueExt for LuaValue {
607 fn base_type(&self) -> lua_types::LuaType { self.type_tag() }
608 fn to_number_no_strconv(&self) -> Option<f64> {
609 match self {
610 LuaValue::Float(f) => Some(*f),
611 LuaValue::Int(i) => Some(*i as f64),
612 _ => None,
613 }
614 }
615 fn to_number_with_strconv(&self) -> Option<f64> {
616 if let Some(n) = self.to_number_no_strconv() { return Some(n); }
617 if let LuaValue::Str(s) = self {
618 let mut tmp = LuaValue::Nil;
619 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
620 if sz == 0 { return None; }
621 return match tmp {
622 LuaValue::Int(i) => Some(i as f64),
623 LuaValue::Float(f) => Some(f),
624 _ => None,
625 };
626 }
627 None
628 }
629 fn to_integer_no_strconv(&self) -> Option<i64> {
630 match self {
631 LuaValue::Int(i) => Some(*i),
632 LuaValue::Float(f) if f.fract() == 0.0 && f.is_finite() => {
633 let min_f = i64::MIN as f64;
637 let max_plus1_f = -(i64::MIN as f64);
638 if *f >= min_f && *f < max_plus1_f {
639 Some(*f as i64)
640 } else {
641 None
642 }
643 }
644 _ => None,
645 }
646 }
647 fn to_integer_with_strconv(&self) -> Option<i64> {
648 if let Some(i) = self.to_integer_no_strconv() { return Some(i); }
649 if let LuaValue::Str(s) = self {
650 let mut tmp = LuaValue::Nil;
651 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
652 if sz == 0 { return None; }
653 return tmp.to_integer_no_strconv();
654 }
655 None
656 }
657 fn full_type_tag(&self) -> u8 {
658 match self {
659 LuaValue::Nil => 0x00,
660 LuaValue::Bool(false) => 0x01,
661 LuaValue::Bool(true) => 0x11,
662 LuaValue::Int(_) => 0x03,
663 LuaValue::Float(_) => 0x13,
664 LuaValue::Str(s) if s.is_short() => 0x04,
665 LuaValue::Str(_) => 0x14,
666 LuaValue::LightUserData(_) => 0x02,
667 LuaValue::Table(_) => 0x05,
668 LuaValue::Function(LuaClosure::Lua(_)) => 0x06,
669 LuaValue::Function(LuaClosure::LightC(_)) => 0x16,
670 LuaValue::Function(LuaClosure::C(_)) => 0x26,
671 LuaValue::UserData(_) => 0x07,
672 LuaValue::Thread(_) => 0x08,
673 }
674 }
675}
676
677pub trait LuaTypeExt {
679 fn type_name(&self) -> &'static [u8];
680}
681
682impl LuaTypeExt for lua_types::LuaType {
683 fn type_name(&self) -> &'static [u8] {
684 use lua_types::LuaType::*;
685 match self {
686 None => b"no value",
687 Nil => b"nil",
688 Boolean => b"boolean",
689 LightUserData => b"userdata",
690 Number => b"number",
691 String => b"string",
692 Table => b"table",
693 Function => b"function",
694 UserData => b"userdata",
695 Thread => b"thread",
696 }
697 }
698}
699
700pub trait StackIdxExt {
704 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32;
705 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32;
706 fn raw(self) -> u32;
707}
708impl StackIdxExt for StackIdx {
709 #[inline(always)]
710 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.saturating_sub(n.into().0.0) }
711 #[inline(always)]
712 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.wrapping_sub(n.into().0.0) }
713 #[inline(always)]
714 fn raw(self) -> u32 { self.0 }
715}
716
717pub trait LuaTableRefExt {
727 fn metatable(&self) -> Option<GcRef<LuaTable>>;
728 fn as_ptr(&self) -> *const ();
729 fn get(&self, _k: &LuaValue) -> LuaValue;
730 fn get_int(&self, _k: i64) -> LuaValue;
731 fn get_short_str(&self, _k: &GcRef<LuaString>) -> LuaValue;
732 fn raw_set(&self, _state: &mut LuaState, _k: LuaValue, _v: LuaValue) -> Result<(), LuaError>;
733 fn raw_set_int(&self, _state: &mut LuaState, _k: i64, _v: LuaValue) -> Result<(), LuaError>;
734 fn invalidate_tm_cache(&self);
735 fn resize(&self, _state: &mut LuaState, _na: usize, _nh: usize) -> Result<(), LuaError>;
736 fn next(&self, _k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError>;
737}
738impl LuaTableRefExt for GcRef<LuaTable> {
739 #[inline]
740 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
741 #[inline]
742 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
743 #[inline]
744 fn get(&self, k: &LuaValue) -> LuaValue { (**self).get(k) }
745 #[inline]
746 fn get_int(&self, k: i64) -> LuaValue { (**self).get_int(k) }
747 #[inline]
748 fn get_short_str(&self, k: &GcRef<LuaString>) -> LuaValue { (**self).get_short_str(k) }
749 #[inline]
752 fn raw_set(&self, _state: &mut LuaState, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
753 let before = (**self).buffer_bytes();
754 let result = (**self).try_raw_set(k, v);
755 if result.is_ok() {
756 account_table_buffer_delta(self, before);
757 }
758 result
759 }
760 #[inline]
761 fn raw_set_int(&self, _state: &mut LuaState, k: i64, v: LuaValue) -> Result<(), LuaError> {
762 let before = (**self).buffer_bytes();
763 let result = (**self).try_raw_set_int(k, v);
764 if result.is_ok() {
765 account_table_buffer_delta(self, before);
766 }
767 result
768 }
769 fn invalidate_tm_cache(&self) {}
770 fn resize(&self, _state: &mut LuaState, na: usize, nh: usize) -> Result<(), LuaError> {
771 let before = (**self).buffer_bytes();
772 let na32 = na.min(u32::MAX as usize) as u32;
773 let nh32 = nh.min(u32::MAX as usize) as u32;
774 let result = (**self).resize(na32, nh32);
775 if result.is_ok() {
776 account_table_buffer_delta(self, before);
777 }
778 result
779 }
780 fn next(&self, k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError> {
781 (**self).try_next_pair(&k)
782 }
783}
784
785#[inline]
786fn account_table_buffer_delta(t: &GcRef<LuaTable>, before: usize) {
787 let after = (**t).buffer_bytes();
788 if after > before {
789 t.account_buffer((after - before) as isize);
790 } else if before > after {
791 t.account_buffer(-((before - after) as isize));
792 }
793}
794
795pub trait LuaUserDataRefExt {
796 fn metatable(&self) -> Option<GcRef<LuaTable>>;
797 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>);
798 fn as_ptr(&self) -> *const ();
799 fn len(&self) -> usize;
800}
801impl LuaUserDataRefExt for GcRef<LuaUserData> {
802 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
803 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>) { (**self).set_metatable(mt); }
804 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
805 fn len(&self) -> usize { self.0.data.len() }
806}
807
808pub trait LuaStringRefExt {
809 fn is_white(&self) -> bool;
810 fn hash(&self) -> u32;
811 fn as_gc_ref(&self) -> GcRef<LuaString>;
812}
813impl LuaStringRefExt for GcRef<LuaString> {
814 fn is_white(&self) -> bool { false }
815 fn hash(&self) -> u32 { self.0.hash() }
816 fn as_gc_ref(&self) -> GcRef<LuaString> { self.clone() }
817}
818
819pub trait LuaLClosureRefExt {
820 fn proto(&self) -> &GcRef<LuaProto>;
821 fn nupvalues(&self) -> usize;
822}
823impl LuaLClosureRefExt for GcRef<lua_types::closure::LuaLClosure> {
824 fn proto(&self) -> &GcRef<LuaProto> { &self.0.proto }
825 fn nupvalues(&self) -> usize { self.0.upvals.len() }
826}
827
828pub trait LuaClosureExt {
830 fn nupvalues(&self) -> usize;
831}
832impl LuaClosureExt for LuaClosure {
833 fn nupvalues(&self) -> usize {
834 match self {
835 LuaClosure::Lua(l) => l.0.upvals.len(),
836 LuaClosure::C(c) => c.0.upvalues.borrow().len(),
837 LuaClosure::LightC(_) => 0,
838 }
839 }
840}
841
842pub trait LuaProtoExt {
844 fn source_bytes(&self) -> &[u8];
845 fn source_string(&self) -> Option<&GcRef<LuaString>>;
846}
847impl LuaProtoExt for LuaProto {
848 fn source_bytes(&self) -> &[u8] {
849 match &self.source { Some(s) => s.0.as_bytes(), None => &[] }
850 }
851 fn source_string(&self) -> Option<&GcRef<LuaString>> { self.source.as_ref() }
852}
853
854pub trait Collectable: std::fmt::Debug {}
861
862impl std::fmt::Debug for LuaState {
863 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
864 write!(f, "LuaState")
865 }
866}
867impl Collectable for LuaState {}
868
869pub type ParserHook = fn(
878 state: &mut LuaState,
879 source: &[u8],
880 name: &[u8],
881 firstchar: i32,
882) -> Result<GcRef<lua_types::closure::LuaLClosure>, LuaError>;
883
884pub type FileLoaderHook = fn(filename: &[u8]) -> Result<Vec<u8>, LuaError>;
892
893pub type FileOpenHook =
904 fn(filename: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
905
906pub type OutputHook = fn(bytes: &[u8]) -> std::io::Result<()>;
914
915pub type InputHook = fn(buf: &mut [u8]) -> std::io::Result<usize>;
918
919pub type EnvHook = fn(name: &[u8]) -> Option<Vec<u8>>;
925
926pub type UnixTimeHook = fn() -> i64;
928
929pub type CpuClockHook = fn() -> f64;
937
938pub type LocalOffsetHook = fn(timestamp: i64) -> i64;
950
951pub type EntropyHook = fn() -> u64;
955
956pub type TempNameHook = fn() -> Result<Vec<u8>, LuaError>;
961
962pub type PopenHook =
973 fn(cmd: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
974
975pub type FileRemoveHook = fn(filename: &[u8]) -> Result<(), LuaError>;
981
982pub type FileRenameHook = fn(from: &[u8], to: &[u8]) -> Result<(), LuaError>;
988
989#[derive(Clone, Copy, Debug)]
995pub enum OsExecuteReason {
996 Exit,
998 Signal,
1000}
1001
1002#[derive(Debug)]
1005pub struct OsExecuteResult {
1006 pub success: bool,
1008 pub reason: OsExecuteReason,
1010 pub code: i32,
1012}
1013
1014pub type OsExecuteHook = fn(cmd: &[u8]) -> Result<OsExecuteResult, LuaError>;
1021
1022#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1029pub struct DynLibId(pub u64);
1030
1031pub enum DynamicSymbol {
1039 RustNative(LuaCFunction),
1042 LuaCAbi(*const ()),
1048 Unsupported { reason: Vec<u8> },
1051}
1052
1053pub type DynLibLoadHook =
1064 fn(state: &mut LuaState, path: &[u8], see_global: bool) -> Result<DynLibId, LuaError>;
1065
1066pub type DynLibSymbolHook =
1074 fn(state: &mut LuaState, handle: DynLibId, symbol: &[u8]) -> Result<DynamicSymbol, LuaError>;
1075
1076pub type DynLibUnloadHook = fn(handle: DynLibId);
1084
1085pub struct ThreadRegistryEntry {
1091 pub state: Rc<RefCell<LuaState>>,
1096 pub value: GcRef<lua_types::value::LuaThread>,
1099}
1100
1101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1106pub struct ExternalRootKey {
1107 index: usize,
1108 generation: u64,
1109}
1110
1111#[derive(Debug)]
1112struct ExternalRootSlot {
1113 value: Option<LuaValue>,
1114 generation: u64,
1115}
1116
1117#[derive(Debug, Default)]
1123pub struct ExternalRootSet {
1124 slots: Vec<ExternalRootSlot>,
1125 free: Vec<usize>,
1126 live: usize,
1127}
1128
1129impl ExternalRootSet {
1130 pub fn insert(&mut self, value: LuaValue) -> ExternalRootKey {
1131 if let Some(index) = self.free.pop() {
1132 let slot = &mut self.slots[index];
1133 debug_assert!(
1134 slot.value.is_none(),
1135 "free external-root slot is occupied"
1136 );
1137 slot.generation = slot.generation.wrapping_add(1).max(1);
1138 slot.value = Some(value);
1139 self.live += 1;
1140 ExternalRootKey {
1141 index,
1142 generation: slot.generation,
1143 }
1144 } else {
1145 let index = self.slots.len();
1146 self.slots.push(ExternalRootSlot {
1147 value: Some(value),
1148 generation: 1,
1149 });
1150 self.live += 1;
1151 ExternalRootKey {
1152 index,
1153 generation: 1,
1154 }
1155 }
1156 }
1157
1158 pub fn get(&self, key: ExternalRootKey) -> Option<&LuaValue> {
1159 let slot = self.slots.get(key.index)?;
1160 if slot.generation == key.generation {
1161 slot.value.as_ref()
1162 } else {
1163 None
1164 }
1165 }
1166
1167 pub fn replace(&mut self, key: ExternalRootKey, value: LuaValue) -> Option<LuaValue> {
1168 let slot = self.slots.get_mut(key.index)?;
1169 if slot.generation != key.generation || slot.value.is_none() {
1170 return None;
1171 }
1172 slot.value.replace(value)
1173 }
1174
1175 pub fn remove(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
1176 let slot = self.slots.get_mut(key.index)?;
1177 if slot.generation != key.generation {
1178 return None;
1179 }
1180 let old = slot.value.take()?;
1181 self.free.push(key.index);
1182 self.live -= 1;
1183 Some(old)
1184 }
1185
1186 pub fn iter_values(&self) -> impl Iterator<Item = &LuaValue> {
1187 self.slots.iter().filter_map(|slot| slot.value.as_ref())
1188 }
1189
1190 pub fn len(&self) -> usize {
1191 self.live
1192 }
1193
1194 pub fn is_empty(&self) -> bool {
1195 self.live == 0
1196 }
1197
1198 pub fn vacant_len(&self) -> usize {
1199 self.free.len()
1200 }
1201}
1202
1203pub struct GlobalState {
1209 pub parser_hook: Option<ParserHook>,
1215
1216 pub cli_argv: Option<Vec<Vec<u8>>>,
1223
1224 pub cli_preload: Option<fn(&mut LuaState) -> Result<(), LuaError>>,
1228
1229 pub lua_version: lua_types::LuaVersion,
1236
1237 pub file_loader_hook: Option<FileLoaderHook>,
1242
1243 pub file_open_hook: Option<FileOpenHook>,
1248
1249 pub stdout_hook: Option<OutputHook>,
1253
1254 pub stderr_hook: Option<OutputHook>,
1256
1257 pub stdin_hook: Option<InputHook>,
1260
1261 pub env_hook: Option<EnvHook>,
1263
1264 pub unix_time_hook: Option<UnixTimeHook>,
1267
1268 pub cpu_clock_hook: Option<CpuClockHook>,
1271
1272 pub local_offset_hook: Option<LocalOffsetHook>,
1277
1278 pub entropy_hook: Option<EntropyHook>,
1281
1282 pub temp_name_hook: Option<TempNameHook>,
1284
1285 pub popen_hook: Option<PopenHook>,
1290
1291 pub file_remove_hook: Option<FileRemoveHook>,
1294
1295 pub file_rename_hook: Option<FileRenameHook>,
1298
1299 pub os_execute_hook: Option<OsExecuteHook>,
1303
1304 pub dynlib_load_hook: Option<DynLibLoadHook>,
1309
1310 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
1314
1315 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
1319
1320 pub sandbox: SandboxLimits,
1323
1324 pub gc_debt: isize,
1326
1327 pub gc_estimate: usize,
1328
1329 pub lastatomic: usize,
1331
1332 pub strt: StringPool,
1334
1335 pub l_registry: LuaValue,
1337
1338 pub external_roots: ExternalRootSet,
1341
1342 pub globals: LuaValue,
1349 pub loaded: LuaValue,
1350
1351 pub nilvalue: LuaValue,
1355
1356 pub seed: u32,
1358
1359 pub currentwhite: u8,
1361
1362 pub gcstate: u8,
1363
1364 pub gckind: u8,
1365
1366 pub gcstopem: bool,
1367
1368 pub genminormul: u8,
1370
1371 pub genmajormul: u8,
1372
1373 pub gcstp: u8,
1374
1375 pub gcemergency: bool,
1376
1377 pub gcpause: u8,
1379
1380 pub gcstepmul: u8,
1382
1383 pub gcstepsize: u8,
1384
1385 pub gc55_params: [i64; 6],
1394
1395 pub sweepgc_cursor: usize,
1400
1401 pub weak_tables_registry: lua_gc::WeakRegistry<WeakTableEntry>,
1410
1411 pub finalizers: lua_gc::FinalizerRegistry<FinalizerObject>,
1414
1415 pub gc_finalizer_error: Option<LuaValue>,
1426
1427 pub twups: Vec<GcRef<LuaState>>,
1437
1438 pub panic: Option<LuaCFunction>,
1440
1441 pub mainthread: Option<GcRef<LuaState>>,
1444
1445 pub threads: std::collections::HashMap<u64, ThreadRegistryEntry>,
1458
1459 pub main_thread_value: GcRef<lua_types::value::LuaThread>,
1464
1465 pub current_thread_id: u64,
1470
1471 pub main_thread_id: u64,
1474
1475 pub next_thread_id: u64,
1478
1479 pub memerrmsg: GcRef<LuaString>,
1481
1482 pub tmname: Vec<GcRef<LuaString>>,
1485
1486 pub mt: [Option<GcRef<LuaTable>>; LUA_NUMTYPES],
1488
1489 pub strcache: [[GcRef<LuaString>; STRCACHE_M]; STRCACHE_N],
1491
1492 pub interned_lt: InternedStringMap,
1498
1499 pub warnf: Option<Box<dyn FnMut(&[u8], bool)>>,
1501
1502 pub warn_mode: WarnMode,
1507
1508 pub test_warn_enabled: bool,
1513 pub test_warn_on: bool,
1514 pub test_warn_mode: TestWarnMode,
1515 pub test_warn_last_to_cont: bool,
1516 pub test_warn_buffer: Vec<u8>,
1517
1518 pub c_functions: Vec<LuaCallable>,
1523
1524 pub heap: lua_gc::Heap,
1529
1530 pub cross_thread_upvals: std::collections::HashMap<(u64, StackIdx), LuaValue>,
1544
1545 pub suspended_parent_stacks: Vec<Vec<LuaValue>>,
1559
1560 pub suspended_parent_open_upvals: Vec<Vec<GcRef<UpVal>>>,
1566}
1567
1568const SANDBOX_COUNT_MASK: u8 = 1 << 3;
1571
1572pub const SANDBOX_TRIP_NONE: u8 = 0;
1574pub const SANDBOX_TRIP_INSTRUCTIONS: u8 = 1;
1576pub const SANDBOX_TRIP_MEMORY: u8 = 2;
1578
1579#[derive(Default)]
1586pub struct SandboxLimits {
1587 pub interval: std::cell::Cell<i32>,
1589 pub instr_limited: std::cell::Cell<bool>,
1591 pub instr_remaining: std::cell::Cell<u64>,
1593 pub instr_limit: std::cell::Cell<u64>,
1595 pub mem_limit: std::cell::Cell<Option<usize>>,
1597 pub tripped: std::cell::Cell<u8>,
1599 pub aborting: std::cell::Cell<bool>,
1604}
1605
1606impl GlobalState {
1607 pub fn sandbox_active(&self) -> bool {
1609 self.sandbox.interval.get() != 0
1610 }
1611
1612 pub fn total_bytes(&self) -> usize {
1617 self.heap.bytes_used().max(1)
1618 }
1619
1620 pub fn get_thread(&self, id: u64) -> Option<&ThreadRegistryEntry> {
1625 self.threads.get(&id)
1626 }
1627
1628 pub fn thread_value_for(&self, id: u64) -> Option<GcRef<lua_types::value::LuaThread>> {
1632 if id == self.main_thread_id {
1633 Some(self.main_thread_value.clone())
1634 } else {
1635 self.threads.get(&id).map(|e| e.value.clone())
1636 }
1637 }
1638
1639 pub fn is_complete(&self) -> bool {
1646 matches!(self.nilvalue, LuaValue::Nil)
1647 }
1648
1649 pub fn current_white(&self) -> u8 {
1657 self.currentwhite
1658 }
1659
1660 pub fn other_white(&self) -> u8 {
1664 self.currentwhite ^ 0x03
1665 }
1666
1667 pub fn is_gen_mode(&self) -> bool {
1671 self.gckind == GcKind::Generational as u8 || self.lastatomic != 0
1672 }
1673
1674 pub fn gc_running(&self) -> bool {
1678 self.gcstp == 0
1679 }
1680
1681 pub fn keep_invariant(&self) -> bool {
1685 self.heap.gc_state().is_invariant()
1686 }
1687
1688 pub fn is_sweep_phase(&self) -> bool {
1692 self.heap.gc_state().is_sweep()
1693 }
1694
1695 pub fn gc_debt(&self) -> isize { self.gc_debt }
1697 pub fn set_gc_debt(&mut self, d: isize) { self.gc_debt = d; }
1698 pub fn gc_at_pause(&self) -> bool { self.heap.gc_state().is_pause() }
1699 fn get_gc_param(p: u8) -> i32 { (p as i32) * 4 }
1700 fn set_gc_param_slot(slot: &mut u8, p: i32) { *slot = (p / 4) as u8; }
1701 pub fn gc_pause_param(&self) -> i32 { Self::get_gc_param(self.gcpause) }
1702 pub fn set_gc_pause_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcpause, p); }
1703 pub fn gc_stepmul_param(&self) -> i32 { Self::get_gc_param(self.gcstepmul) }
1704 pub fn set_gc_stepmul_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcstepmul, p); }
1705 pub fn gc_genmajormul_param(&self) -> i32 { Self::get_gc_param(self.genmajormul) }
1706 pub fn set_gc_genmajormul(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.genmajormul, p); }
1707 pub fn gc55_param(&mut self, idx: usize, value: i64) -> i64 {
1711 let old = self.gc55_params[idx];
1712 if value >= 0 {
1713 self.gc55_params[idx] = value;
1714 }
1715 old
1716 }
1717 pub fn gc_stop_flags(&self) -> u8 { self.gcstp }
1718 pub fn set_gc_stop_flags(&mut self, f: u8) { self.gcstp = f; }
1719 pub fn stop_gc_internal(&mut self) -> u8 {
1720 let old = self.gcstp;
1721 self.gcstp |= GCSTPGC;
1722 old
1723 }
1724 pub fn set_gc_stop_user(&mut self) {
1725 self.gcstp = GCSTPUSR;
1727 }
1728 pub fn clear_gc_stop(&mut self) { self.gcstp = 0; }
1729 pub fn is_gc_running(&self) -> bool { self.gcstp == 0 }
1730 pub fn is_gc_stopped_internally(&self) -> bool { (self.gcstp & GCSTPGC) != 0 }
1735
1736 pub fn tm_name<T: TmIndex>(&self, tm: T) -> Option<GcRef<LuaString>> {
1746 self.tmname.get(tm.tm_index()).cloned()
1747 }
1748}
1749
1750pub trait TmIndex: Copy {
1756 fn tm_index(self) -> usize;
1757}
1758impl TmIndex for lua_types::tagmethod::TagMethod {
1759 fn tm_index(self) -> usize { self as u8 as usize }
1760}
1761impl TmIndex for crate::tagmethods::TagMethod {
1762 fn tm_index(self) -> usize { self as u8 as usize }
1763}
1764impl TmIndex for usize {
1765 fn tm_index(self) -> usize { self }
1766}
1767impl TmIndex for u8 {
1768 fn tm_index(self) -> usize { self as usize }
1769}
1770
1771use lua_types::tagmethod::TagMethod;
1772
1773pub struct LuaState {
1783 pub status: u8,
1787
1788 pub allowhook: bool,
1790
1791 pub nci: u32,
1793
1794 pub top: StackIdx,
1798
1799 pub stack_last: StackIdx,
1801
1802 pub stack: Vec<StackValue>,
1804
1805 pub ci: CallInfoIdx,
1809
1810 pub call_info: Vec<CallInfo>,
1813
1814 pub openupval: Vec<GcRef<UpVal>>,
1818
1819 pub tbclist: Vec<StackIdx>,
1821
1822 pub(crate) global: Rc<RefCell<GlobalState>>,
1827
1828 pub hook: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>,
1832
1833 pub hookmask: u8,
1835
1836 pub basehookcount: i32,
1838
1839 pub hookcount: i32,
1841
1842 pub errfunc: isize,
1849
1850 pub n_ccalls: u32,
1854
1855 pub oldpc: u32,
1859
1860 pub marked: u8,
1864
1865 pub cached_thread_id: u64,
1881
1882 pub gc_check_needed: bool,
1887
1888}
1889
1890impl LuaState {
1891 pub fn global(&self) -> std::cell::Ref<'_, GlobalState> {
1899 self.global.borrow()
1900 }
1901
1902 pub fn global_mut(&self) -> std::cell::RefMut<'_, GlobalState> {
1906 self.global.borrow_mut()
1907 }
1908
1909 pub fn global_rc(&self) -> Rc<RefCell<GlobalState>> {
1913 Rc::clone(&self.global)
1914 }
1915
1916 pub fn enable_test_warning_handler(&mut self) -> Result<(), LuaError> {
1918 {
1919 let mut g = self.global_mut();
1920 g.test_warn_enabled = true;
1921 g.test_warn_on = false;
1922 g.test_warn_mode = TestWarnMode::Normal;
1923 g.test_warn_last_to_cont = false;
1924 g.test_warn_buffer.clear();
1925 }
1926 self.push(LuaValue::Bool(false));
1927 crate::api::set_global(self, b"_WARN")
1928 }
1929
1930 pub fn c_calls(&self) -> u32 {
1934 self.n_ccalls & 0xffff
1935 }
1936
1937 pub fn inc_nny(&mut self) {
1941 self.n_ccalls += 0x10000;
1942 }
1943
1944 pub fn dec_nny(&mut self) {
1948 self.n_ccalls -= 0x10000;
1949 }
1950
1951 pub fn is_yieldable(&self) -> bool {
1955 (self.n_ccalls & 0xffff0000) == 0
1956 }
1957
1958 pub fn reset_hook_count(&mut self) {
1962 self.hookcount = self.basehookcount;
1963 }
1964
1965 pub fn install_sandbox_limits(
1974 &mut self,
1975 interval: i32,
1976 instr_limit: Option<u64>,
1977 mem_limit: Option<usize>,
1978 ) {
1979 let interval = interval.max(1);
1980 {
1981 let g = self.global();
1982 g.sandbox.interval.set(interval);
1983 g.sandbox.instr_limited.set(instr_limit.is_some());
1984 g.sandbox.instr_remaining.set(instr_limit.unwrap_or(0));
1985 g.sandbox.instr_limit.set(instr_limit.unwrap_or(0));
1986 g.sandbox.mem_limit.set(mem_limit);
1987 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
1988 }
1989 self.hookmask |= SANDBOX_COUNT_MASK;
1990 self.basehookcount = interval;
1991 self.hookcount = interval;
1992 crate::debug::arm_traps(self);
1993 }
1994
1995 pub fn sandbox_charge_interval(&self) -> Option<LuaError> {
2000 let interval = self.global().sandbox.interval.get();
2001 self.sandbox_charge(interval as u64)
2002 }
2003
2004 pub fn sandbox_charge(&self, amount: u64) -> Option<LuaError> {
2013 let g = self.global();
2014 if g.sandbox.interval.get() == 0 {
2015 return None;
2016 }
2017 if g.sandbox.instr_limited.get() {
2018 let rem = g.sandbox.instr_remaining.get().saturating_sub(amount);
2019 g.sandbox.instr_remaining.set(rem);
2020 if rem == 0 {
2021 g.sandbox.tripped.set(SANDBOX_TRIP_INSTRUCTIONS);
2022 g.sandbox.aborting.set(true);
2023 return Some(LuaError::runtime(format_args!(
2024 "sandbox: instruction budget exhausted"
2025 )));
2026 }
2027 }
2028 if let Some(limit) = g.sandbox.mem_limit.get() {
2029 if g.total_bytes() > limit {
2030 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2031 g.sandbox.aborting.set(true);
2032 return Some(LuaError::runtime(format_args!(
2033 "sandbox: memory limit exceeded"
2034 )));
2035 }
2036 }
2037 None
2038 }
2039
2040 pub fn sandbox_reserve(&self, additional: usize) -> Option<LuaError> {
2048 let g = self.global();
2049 if g.sandbox.interval.get() == 0 {
2050 return None;
2051 }
2052 if let Some(limit) = g.sandbox.mem_limit.get() {
2053 let projected = g.total_bytes().saturating_add(additional);
2054 if projected > limit {
2055 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2056 g.sandbox.aborting.set(true);
2057 return Some(LuaError::runtime(format_args!(
2058 "sandbox: memory limit exceeded"
2059 )));
2060 }
2061 }
2062 None
2063 }
2064
2065 pub fn sandbox_match_step_limit(&self) -> u64 {
2070 let g = self.global();
2071 if g.sandbox.interval.get() != 0 && g.sandbox.instr_limited.get() {
2072 g.sandbox.instr_remaining.get()
2073 } else {
2074 0
2075 }
2076 }
2077
2078 pub fn sandbox_aborting(&self) -> bool {
2082 self.global().sandbox.aborting.get()
2083 }
2084
2085 pub fn sandbox_instr_limited(&self) -> bool {
2087 self.global().sandbox.instr_limited.get()
2088 }
2089
2090 pub fn sandbox_instr_remaining(&self) -> u64 {
2093 self.global().sandbox.instr_remaining.get()
2094 }
2095
2096 pub fn sandbox_instr_limit(&self) -> u64 {
2098 self.global().sandbox.instr_limit.get()
2099 }
2100
2101 pub fn sandbox_tripped_code(&self) -> u8 {
2103 self.global().sandbox.tripped.get()
2104 }
2105
2106 pub fn sandbox_reset(&self) {
2109 let g = self.global();
2110 if g.sandbox.instr_limited.get() {
2111 g.sandbox.instr_remaining.set(g.sandbox.instr_limit.get());
2112 }
2113 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2114 g.sandbox.aborting.set(false);
2115 }
2116
2117 pub fn stack_size(&self) -> usize {
2121 self.stack_last.0 as usize
2122 }
2123
2124 #[inline(always)]
2128 pub fn push(&mut self, val: LuaValue) {
2129 let top = self.top.0 as usize;
2130 if top < self.stack.len() {
2131 self.stack[top] = StackValue { val, tbc_delta: 0 };
2132 } else {
2133 self.stack.push(StackValue { val, tbc_delta: 0 });
2134 }
2135 self.top = StackIdx(self.top.0 + 1);
2136 }
2137
2138 #[inline(always)]
2141 pub fn pop(&mut self) -> LuaValue {
2142 if self.top.0 == 0 {
2143 return LuaValue::Nil;
2144 }
2145 self.top = StackIdx(self.top.0 - 1);
2146 self.stack[self.top.0 as usize].val.clone()
2147 }
2148
2149 #[inline(always)]
2153 pub fn stack_val(&self, idx: StackIdx) -> &LuaValue {
2154 &self.stack[idx.0 as usize].val
2155 }
2156
2157 #[inline(always)]
2159 pub fn set_stack_val(&mut self, idx: StackIdx, val: LuaValue) {
2160 self.stack[idx.0 as usize].val = val;
2161 }
2162
2163 pub fn gc(&mut self) -> GcHandle<'_> {
2170 GcHandle { _state: self }
2171 }
2172
2173 pub fn external_root_value(&mut self, value: LuaValue) -> ExternalRootKey {
2175 self.global_mut().external_roots.insert(value)
2176 }
2177
2178 pub fn external_rooted_value(&self, key: ExternalRootKey) -> Option<LuaValue> {
2180 self.global().external_roots.get(key).cloned()
2181 }
2182
2183 pub fn external_replace_root(
2185 &mut self,
2186 key: ExternalRootKey,
2187 value: LuaValue,
2188 ) -> Option<LuaValue> {
2189 self.global_mut().external_roots.replace(key, value)
2190 }
2191
2192 pub fn external_unroot_value(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
2194 self.global_mut().external_roots.remove(key)
2195 }
2196
2197 pub fn try_external_unroot_value(
2200 &mut self,
2201 key: ExternalRootKey,
2202 ) -> std::result::Result<Option<LuaValue>, std::cell::BorrowMutError> {
2203 self.global
2204 .try_borrow_mut()
2205 .map(|mut global| global.external_roots.remove(key))
2206 }
2207
2208 pub fn new_table(&mut self) -> GcRef<LuaTable> {
2212 self.mark_gc_check_needed();
2214 GcRef::new(LuaTable::placeholder())
2215 }
2216
2217 pub fn new_table_with_sizes(
2222 &mut self,
2223 array_size: u32,
2224 hash_size: u32,
2225 ) -> Result<GcRef<LuaTable>, LuaError> {
2226 self.mark_gc_check_needed();
2227 let t = GcRef::new(LuaTable::placeholder());
2228 self.table_resize(&t, array_size as usize, hash_size as usize)?;
2229 Ok(t)
2230 }
2231
2232 pub fn intern_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2243 if bytes.len() <= crate::string::MAX_SHORT_LEN {
2244 let mut inserted = false;
2245 let interned = {
2246 let key = bytes.to_vec().into_boxed_slice();
2247 let mut global = self.global_mut();
2248 match global.interned_lt.entry(key) {
2249 Entry::Occupied(existing) => existing.get().clone(),
2250 Entry::Vacant(vacant) => {
2251 let new_ref = GcRef::new(LuaString::from_bytes(vacant.key().to_vec()));
2252 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2253 vacant.insert(new_ref.clone());
2254 inserted = true;
2255 new_ref
2256 }
2257 }
2258 };
2259 if inserted {
2260 self.mark_gc_check_needed();
2261 }
2262 Ok(interned)
2263 } else {
2264 self.mark_gc_check_needed();
2265 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2266 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2267 Ok(new_ref)
2268 }
2269 }
2270
2271 #[inline(always)]
2273 pub fn top_idx(&self) -> StackIdx {
2274 self.top
2275 }
2276}
2277
2278impl LuaState {
2287 #[inline(always)]
2288 pub fn get_at(&self, idx: impl Into<StackIdxConv>) -> LuaValue {
2289 let i: StackIdx = idx.into().0;
2290 match self.stack.get(i.0 as usize) {
2291 Some(slot) => slot.val.clone(),
2292 None => LuaValue::Nil,
2293 }
2294 }
2295 #[inline(always)]
2296 pub fn set_at(&mut self, idx: impl Into<StackIdxConv>, v: LuaValue) {
2297 let i: StackIdx = idx.into().0;
2298 self.stack[i.0 as usize].val = v;
2299 }
2300
2301 pub fn clear_stack_range(&mut self, start: StackIdx, end: StackIdx) {
2307 if end.0 <= start.0 {
2308 return;
2309 }
2310 let end_u = end.0 as usize;
2311 if end_u > self.stack.len() {
2312 self.stack.resize_with(end_u, StackValue::default);
2313 }
2314 for i in start.0..end.0 {
2315 self.stack[i as usize].val = LuaValue::Nil;
2316 self.stack[i as usize].tbc_delta = 0;
2317 }
2318 }
2319 #[inline(always)]
2327 pub fn get_int_at(&self, idx: impl Into<StackIdxConv>) -> Option<i64> {
2328 let i: StackIdx = idx.into().0;
2329 match self.stack.get(i.0 as usize) {
2330 Some(slot) => match &slot.val {
2331 LuaValue::Int(v) => Some(*v),
2332 _ => None,
2333 },
2334 None => None,
2335 }
2336 }
2337 #[inline(always)]
2344 pub fn get_int_pair_at(
2345 &self,
2346 rb: impl Into<StackIdxConv>,
2347 rc: impl Into<StackIdxConv>,
2348 ) -> Option<(i64, i64)> {
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::Int(ib), LuaValue::Int(ic)) => Some((ib, ic)),
2356 _ => None,
2357 }
2358 }
2359 #[inline(always)]
2364 pub fn get_num_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2365 let i: StackIdx = idx.into().0;
2366 match self.stack.get(i.0 as usize) {
2367 Some(slot) => match &slot.val {
2368 LuaValue::Float(f) => Some(*f),
2369 LuaValue::Int(v) => Some(*v as f64),
2370 _ => None,
2371 },
2372 None => None,
2373 }
2374 }
2375 #[inline(always)]
2380 pub fn get_float_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2381 let i: StackIdx = idx.into().0;
2382 match self.stack.get(i.0 as usize) {
2383 Some(slot) => match &slot.val {
2384 LuaValue::Float(f) => Some(*f),
2385 _ => None,
2386 },
2387 None => None,
2388 }
2389 }
2390 #[inline(always)]
2395 pub fn get_num_pair_at(
2396 &self,
2397 rb: impl Into<StackIdxConv>,
2398 rc: impl Into<StackIdxConv>,
2399 ) -> Option<(f64, f64)> {
2400 let rb: StackIdx = rb.into().0;
2401 let rc: StackIdx = rc.into().0;
2402 match (
2403 self.stack[rb.0 as usize].val,
2404 self.stack[rc.0 as usize].val,
2405 ) {
2406 (LuaValue::Float(nb), LuaValue::Float(nc)) => Some((nb, nc)),
2407 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib as f64, ic as f64)),
2408 (LuaValue::Int(ib), LuaValue::Float(nc)) => Some((ib as f64, nc)),
2409 (LuaValue::Float(nb), LuaValue::Int(ic)) => Some((nb, ic as f64)),
2410 _ => None,
2411 }
2412 }
2413 #[inline(always)]
2426 pub fn set_top(&mut self, idx: impl Into<StackIdxConv>) {
2427 let new_top: StackIdx = idx.into().0;
2428 let new_top_u = new_top.0 as usize;
2429 if new_top_u > self.stack.len() {
2430 self.stack.resize_with(new_top_u, StackValue::default);
2431 }
2432 self.top = new_top;
2433 }
2434 #[inline(always)]
2440 pub fn set_top_idx(&mut self, idx: impl Into<StackIdxConv>) {
2441 let new_top: StackIdx = idx.into().0;
2442 self.top = new_top;
2443 }
2444 #[inline(always)]
2447 pub fn dec_top(&mut self) {
2448 if self.top.0 > 0 {
2449 self.top = StackIdx(self.top.0 - 1);
2450 }
2451 }
2452 #[inline(always)]
2453 pub fn pop_n(&mut self, n: usize) {
2454 let cur = self.top.0 as usize;
2455 let new = cur.saturating_sub(n);
2456 self.top = StackIdx(new as u32);
2457 }
2458 #[inline(always)]
2461 pub fn peek_at(&mut self, idx: impl Into<StackIdxConv>) -> LuaValue {
2462 let i: StackIdx = idx.into().0;
2463 match self.stack.get(i.0 as usize) {
2464 Some(slot) => slot.val.clone(),
2465 None => LuaValue::Nil,
2466 }
2467 }
2468 #[inline(always)]
2472 pub fn peek_top(&mut self) -> LuaValue {
2473 if self.top.0 == 0 {
2474 return LuaValue::Nil;
2475 }
2476 self.stack[(self.top.0 - 1) as usize].val.clone()
2477 }
2478 pub fn peek_string_at_top(&mut self) -> GcRef<LuaString> {
2483 match self.peek_top() {
2484 LuaValue::Str(s) => s,
2485 _ => panic!("peek_string_at_top: top of stack is not a string"),
2486 }
2487 }
2488 pub fn stack_at(&mut self, idx: impl Into<StackIdxConv>) -> &mut LuaValue {
2491 let i: StackIdx = idx.into().0;
2492 &mut self.stack[i.0 as usize].val
2493 }
2494 pub fn stack_set_nil(&mut self, idx: impl Into<StackIdxConv>) {
2497 let i: StackIdx = idx.into().0;
2498 let slot = i.0 as usize;
2499 if slot < self.stack.len() {
2500 self.stack[slot].val = LuaValue::Nil;
2501 }
2502 }
2503 pub fn stack_resize(&mut self, size: usize) -> Result<(), LuaError> {
2511 self.stack.resize_with(size, StackValue::default);
2512 Ok(())
2513 }
2514 pub fn stack_available(&mut self) -> usize {
2515 (self.stack_last.0 as usize).saturating_sub(self.top.0 as usize)
2516 }
2517 pub fn check_stack(&mut self, n: i32) -> Result<(), LuaError> {
2518 let free = (self.stack_last.0 as i32) - (self.top.0 as i32);
2519 if free <= n {
2520 self.grow_stack(n, true)?;
2521 }
2522 Ok(())
2523 }
2524 pub fn grow_stack(&mut self, n: i32, raise_error: bool) -> Result<(), LuaError> {
2533 crate::do_::grow_stack(self, n, raise_error).map(|_| ())
2534 }
2535
2536 #[inline(always)]
2537 pub fn get_ci(&self, idx: CallInfoIdx) -> &CallInfo { &self.call_info[idx.as_usize()] }
2538 #[inline(always)]
2539 pub fn get_ci_mut(&mut self, idx: CallInfoIdx) -> &mut CallInfo { &mut self.call_info[idx.as_usize()] }
2540 #[inline(always)]
2541 pub fn current_call_info(&self) -> &CallInfo { &self.call_info[self.ci.as_usize()] }
2542 #[inline(always)]
2543 pub fn current_call_info_mut(&mut self) -> &mut CallInfo { let i = self.ci.as_usize(); &mut self.call_info[i] }
2544 #[inline(always)]
2545 pub fn current_ci_idx(&self) -> CallInfoIdx { self.ci }
2546 pub fn call_stack_mut(&mut self) -> &mut Vec<CallInfo> { &mut self.call_info }
2547 #[inline(always)]
2548 pub fn next_ci(&mut self) -> Result<CallInfoIdx, LuaError> {
2549 match self.call_info[self.ci.as_usize()].next {
2550 Some(idx) => Ok(idx),
2551 None => Ok(extend_ci(self)),
2552 }
2553 }
2554 #[inline(always)]
2555 pub fn prev_ci(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2556 pub fn get_prev_ci(&self, idx: CallInfoIdx) -> Option<&CallInfo> {
2557 self.call_info[idx.as_usize()]
2558 .previous
2559 .map(|p| &self.call_info[p.as_usize()])
2560 }
2561 #[inline(always)]
2562 pub fn is_base_ci(&self, idx: CallInfoIdx) -> bool { idx.as_usize() == 0 }
2563 #[inline(always)]
2564 pub fn is_current_ci(&self, idx: CallInfoIdx) -> bool { idx == self.ci }
2565 pub fn ci_next_func(&self, idx: CallInfoIdx) -> StackIdx {
2566 let next = self.call_info[idx.as_usize()]
2567 .next
2568 .expect("ci_next_func: no next CallInfo");
2569 self.call_info[next.as_usize()].func
2570 }
2571 #[inline(always)]
2572 pub fn ci_top(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].top }
2573 #[inline(always)]
2574 pub fn ci_trap(&mut self, idx: CallInfoIdx) -> bool {
2575 if let CallInfoFrame::Lua { trap, .. } = self.call_info[idx.as_usize()].u {
2576 trap
2577 } else {
2578 false
2579 }
2580 }
2581 #[inline(always)]
2582 pub fn ci_savedpc(&self, idx: CallInfoIdx) -> u32 { self.call_info[idx.as_usize()].saved_pc() }
2583 #[inline(always)]
2584 pub fn set_ci_savedpc(&mut self, idx: CallInfoIdx, pc: u32) {
2585 self.call_info[idx.as_usize()].set_saved_pc(pc);
2586 }
2587 #[inline(always)]
2588 pub fn set_ci_previous(&mut self, idx: CallInfoIdx) {
2589 self.ci = self.call_info[idx.as_usize()]
2590 .previous
2591 .expect("set_ci_previous: returning frame has no previous CallInfo");
2592 }
2593 #[inline(always)]
2594 pub fn ci_previous(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2595 #[inline(always)]
2596 pub fn ci_adjust_func(&mut self, idx: CallInfoIdx, delta: i32) {
2597 let ci = &mut self.call_info[idx.as_usize()];
2598 ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
2599 }
2600 #[inline(always)]
2601 pub fn ci_base(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].func + 1 }
2602 #[inline(always)]
2603 pub fn ci_is_fresh(&self, idx: CallInfoIdx) -> bool {
2604 (self.call_info[idx.as_usize()].callstatus & CIST_FRESH) != 0
2605 }
2606 #[inline(always)]
2607 pub fn ci_lua_closure(&self, idx: CallInfoIdx) -> Option<GcRef<lua_types::closure::LuaLClosure>> {
2608 let func_idx = self.call_info[idx.as_usize()].func;
2609 match self.stack.get(func_idx.0 as usize).map(|slot| slot.val) {
2610 Some(LuaValue::Function(lua_types::closure::LuaClosure::Lua(cl))) => Some(cl),
2611 _ => None,
2612 }
2613 }
2614 #[inline(always)]
2615 pub fn ci_nextraargs(&self, idx: CallInfoIdx) -> i32 {
2616 self.call_info[idx.as_usize()].nextra_args()
2617 }
2618 #[inline(always)]
2619 pub fn ci_nres(&self, idx: CallInfoIdx) -> i32 {
2620 self.call_info[idx.as_usize()].u2.value
2621 }
2622 #[inline(always)]
2623 pub fn ci_nres_set(&mut self, idx: CallInfoIdx, n: i32) {
2624 self.call_info[idx.as_usize()].u2.value = n;
2625 }
2626 #[inline(always)]
2627 pub fn ci_nresults(&self, idx: CallInfoIdx) -> i32 { self.call_info[idx.as_usize()].nresults as i32 }
2628 pub fn ci_prev_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2629 let pc = self.call_info[idx.as_usize()].saved_pc();
2630 let cl = self.ci_lua_closure(idx)
2631 .expect("ci_prev_instruction: CallInfo does not hold a Lua closure");
2632 cl.proto.code[(pc - 1) as usize]
2633 }
2634 pub fn ci_prev2_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2635 let pc = self.call_info[idx.as_usize()].saved_pc();
2636 let cl = self.ci_lua_closure(idx)
2637 .expect("ci_prev2_instruction: CallInfo does not hold a Lua closure");
2638 cl.proto.code[(pc - 2) as usize]
2639 }
2640 pub fn ci_skip_next_instruction(&mut self, idx: CallInfoIdx) {
2641 let pc = self.call_info[idx.as_usize()].saved_pc();
2642 self.call_info[idx.as_usize()].set_saved_pc(pc + 1);
2643 }
2644 pub fn ci_step_pc_back(&mut self, idx: CallInfoIdx) {
2645 let pc = self.call_info[idx.as_usize()].saved_pc();
2646 self.call_info[idx.as_usize()].set_saved_pc(pc - 1);
2647 }
2648 pub fn get_ci_pcrel(&mut self, idx: CallInfoIdx) -> u32 {
2649 self.call_info[idx.as_usize()].saved_pc().saturating_sub(1)
2650 }
2651 pub fn get_ci_u2_funcidx(&mut self, idx: CallInfoIdx) -> i32 {
2652 self.call_info[idx.as_usize()].u2.value
2653 }
2654 pub fn get_ci_u2_nres(&mut self, idx: CallInfoIdx) -> i32 {
2655 self.call_info[idx.as_usize()].u2.value
2656 }
2657 pub fn get_ci_u2_nyield(&mut self, idx: CallInfoIdx) -> i32 {
2658 self.call_info[idx.as_usize()].u2.value
2659 }
2660 pub fn get_ci_vararg_info(&mut self, idx: CallInfoIdx) -> (bool, i32, i32) {
2661 let nextraargs = self.call_info[idx.as_usize()].nextra_args();
2662 match self.ci_lua_closure(idx) {
2663 Some(cl) => (cl.proto.is_vararg, nextraargs, cl.proto.numparams as i32),
2664 None => (false, nextraargs, 0),
2665 }
2666 }
2667 pub fn get_ci_lua_proto_numparams(&mut self, idx: CallInfoIdx) -> u8 {
2668 self.ci_lua_closure(idx)
2669 .map(|cl| cl.proto.numparams)
2670 .unwrap_or(0)
2671 }
2672 pub fn set_ci_u2_nres(&mut self, idx: CallInfoIdx, n: i32) {
2673 self.call_info[idx.as_usize()].u2.value = n;
2674 }
2675 pub fn set_ci_u2_nyield(&mut self, idx: CallInfoIdx, n: i32) {
2676 self.call_info[idx.as_usize()].u2.value = n;
2677 }
2678 pub fn set_ci_transfer_info(&mut self, idx: CallInfoIdx, ftransfer: u16, ntransfer: u16) {
2679 let ci = &mut self.call_info[idx.as_usize()];
2680 ci.u2.ftransfer = ftransfer;
2681 ci.u2.ntransfer = ntransfer;
2682 }
2683 pub fn shrink_ci(&mut self) { shrink_ci(self) }
2684 pub fn check_c_stack(&mut self) -> Result<(), LuaError> { check_c_stack(self) }
2685
2686 pub fn status(&mut self) -> LuaStatus { LuaStatus::from_raw(self.status as i32) }
2687 pub fn errfunc(&mut self) -> isize { self.errfunc }
2688 pub fn old_pc(&mut self) -> u32 { self.oldpc }
2689 pub fn set_old_pc(&mut self, pc: u32) { self.oldpc = pc; }
2690 pub fn set_oldpc(&mut self, pc: u32) { self.oldpc = pc; }
2691 pub fn _hook_call_noargs(&mut self) {}
2692 pub fn hook(&self) -> Option<&Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>> {
2693 self.hook.as_ref()
2694 }
2695 pub fn has_hook(&mut self) -> bool { self.hook.is_some() }
2696 pub fn hook_count(&mut self) -> i32 { self.hookcount }
2697 pub fn set_hook_count(&mut self, n: i32) { self.hookcount = n; }
2698 pub fn hook_mask(&self) -> u8 { self.hookmask }
2699 pub fn set_hook_mask(&mut self, m: u8) { self.hookmask = m; }
2700 pub fn base_hook_count(&self) -> i32 { self.basehookcount }
2701 pub fn set_base_hook_count(&mut self, n: i32) { self.basehookcount = n; }
2702 pub fn set_hook(&mut self, h: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>) {
2703 self.hook = h;
2704 }
2705 pub fn call_hook_event(&mut self, event: i32, line: i32) -> Result<(), LuaError> {
2706 crate::do_::hook(self, event, line, 0, 0)
2707 }
2708
2709 pub fn registry_value(&self) -> LuaValue { self.global().l_registry.clone() }
2710 pub fn registry_get(&self, key: usize) -> LuaValue {
2711 let reg = self.global().l_registry.clone();
2712 match reg {
2713 LuaValue::Table(t) => t.get(&LuaValue::Int(key as i64)),
2714 _ => LuaValue::Nil,
2715 }
2716 }
2717
2718 pub fn new_string(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> { self.intern_or_create_str(bytes) }
2719
2720 pub fn new_proto(&mut self) -> GcRef<LuaProto> {
2732 self.mark_gc_check_needed();
2733 GcRef::new(LuaProto::placeholder())
2734 }
2735
2736 pub fn new_lclosure(&mut self, proto: GcRef<LuaProto>, nupvals: usize) -> GcRef<LuaClosureLua> {
2738 self.mark_gc_check_needed();
2739 let mut upvals = Vec::with_capacity(nupvals);
2740 for _ in 0..nupvals {
2741 upvals.push(std::cell::Cell::new(self.new_upval_closed(LuaValue::Nil)));
2742 }
2743 let closure = GcRef::new(LuaClosureLua { proto, upvals });
2744 closure.account_buffer(closure.buffer_bytes() as isize);
2745 closure
2746 }
2747
2748 pub fn new_upval_closed(&mut self, v: LuaValue) -> GcRef<UpVal> {
2750 self.mark_gc_check_needed();
2751 GcRef::new(UpVal::closed(v))
2752 }
2753
2754 pub fn new_upval_open(&mut self, thread_id: usize, level: StackIdx) -> GcRef<UpVal> {
2756 self.mark_gc_check_needed();
2757 GcRef::new(UpVal::open(thread_id, level))
2758 }
2759 pub fn intern_or_create_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2766 self.intern_str(bytes)
2767 }
2768 pub fn new_userdata(&mut self, _size: usize, _nuvalue: usize) -> Result<GcRef<LuaUserData>, LuaError> {
2769 Err(LuaError::runtime(format_args!("new_userdata not implemented in this Phase-B build; use new_userdata_typed instead")))
2770 }
2771 pub fn new_c_closure(&mut self, _f: LuaCFunction, _n: i32) -> Result<LuaClosure, LuaError> {
2772 Err(LuaError::runtime(format_args!("new_c_closure not implemented in this Phase-B build; use push_cclosure in lua_vm::api instead")))
2773 }
2774 pub fn push_closure(
2775 &mut self,
2776 proto_idx: usize,
2777 ci: CallInfoIdx,
2778 base: StackIdx,
2779 ra: StackIdx,
2780 ) -> Result<(), LuaError> {
2781 let parent_cl = self.ci_lua_closure(ci).expect(
2782 "push_closure: current frame is not a Lua closure",
2783 );
2784 let child_proto = parent_cl.proto.p[proto_idx].clone();
2785 let nup = child_proto.upvalues.len();
2786 let mut upvals: Vec<std::cell::Cell<GcRef<UpVal>>> = Vec::with_capacity(nup);
2787 for i in 0..nup {
2788 let desc = &child_proto.upvalues[i];
2789 let uv = if desc.instack {
2790 let level = base + desc.idx as i32;
2791 crate::func::find_upval(self, level)
2792 } else {
2793 parent_cl.upval(desc.idx as usize)
2794 };
2795 upvals.push(std::cell::Cell::new(uv));
2796 }
2797 let cache_enabled = matches!(
2801 self.global().lua_version,
2802 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
2803 );
2804 if cache_enabled {
2805 if let Some(cached) = child_proto.cache.borrow().as_ref() {
2806 if cached.upvals.len() == nup
2807 && (0..nup).all(|i| GcRef::ptr_eq(&cached.upvals[i].get(), &upvals[i].get()))
2808 {
2809 let reused = cached.clone();
2810 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(reused)));
2811 return Ok(());
2812 }
2813 }
2814 }
2815 self.mark_gc_check_needed();
2818 let new_cl = GcRef::new(LuaClosureLua {
2819 proto: child_proto.clone(),
2820 upvals,
2821 });
2822 new_cl.account_buffer(new_cl.buffer_bytes() as isize);
2823 if cache_enabled {
2824 *child_proto.cache.borrow_mut() = Some(new_cl.clone());
2825 }
2826 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(new_cl)));
2827 Ok(())
2828 }
2829 pub fn new_tbc_upval(&mut self, idx: StackIdx) -> Result<(), LuaError> {
2830 crate::func::new_tbc_upval(self, idx)
2831 }
2832
2833 #[inline(always)]
2854 pub fn upvalue_get(&self, cl: &GcRef<LuaClosureLua>, n: usize) -> LuaValue {
2855 let uv = cl.upval(n);
2856 let (thread_id, idx) = match uv.try_open_payload() {
2857 Some(p) => p,
2858 None => return uv.closed_value(),
2859 };
2860 let current = self.cached_thread_id;
2861 let tid = thread_id as u64;
2862 if tid == current {
2863 return self.stack[idx.0 as usize].val;
2864 }
2865 self.upvalue_get_cross_thread(tid, idx)
2866 }
2867
2868 #[cold]
2869 #[inline(never)]
2870 fn upvalue_get_cross_thread(&self, tid: u64, idx: StackIdx) -> LuaValue {
2871 let entry_rc = {
2872 let g = self.global();
2873 g.threads.get(&tid).map(|e| e.state.clone())
2874 };
2875 if let Some(rc) = entry_rc {
2876 if let Ok(home_state) = rc.try_borrow() {
2877 return home_state.get_at(idx);
2878 }
2879 }
2880 let g = self.global();
2881 match g.cross_thread_upvals.get(&(tid, idx)) {
2882 Some(v) => *v,
2883 None => LuaValue::Nil,
2884 }
2885 }
2886 #[inline(always)]
2895 pub fn upvalue_set(&mut self, cl: &GcRef<LuaClosureLua>, n: usize, val: LuaValue) -> Result<(), LuaError> {
2896 let uv = cl.upval(n);
2897 match uv.try_open_payload() {
2898 Some((thread_id, idx)) => {
2899 let tid = thread_id as u64;
2900 let current = self.cached_thread_id;
2901 if tid == current {
2902 self.stack[idx.0 as usize].val = val;
2903 } else {
2904 self.upvalue_set_cross_thread(tid, idx, val)?;
2905 }
2906 }
2907 None => {
2908 uv.set_closed_value(val);
2909 }
2910 }
2911 if val.is_collectable() {
2912 self.gc_barrier_upval(&uv, &val);
2913 }
2914 Ok(())
2915 }
2916
2917 #[cold]
2918 #[inline(never)]
2919 fn upvalue_set_cross_thread(
2920 &mut self,
2921 tid: u64,
2922 idx: StackIdx,
2923 val: LuaValue,
2924 ) -> Result<(), LuaError> {
2925 let entry_rc = {
2926 let g = self.global();
2927 g.threads.get(&tid).map(|e| e.state.clone())
2928 };
2929 if let Some(rc) = entry_rc {
2930 if let Ok(mut home_state) = rc.try_borrow_mut() {
2931 home_state.set_at(idx, val);
2932 return Ok(());
2933 }
2934 }
2935 let mut g = self.global_mut();
2936 g.cross_thread_upvals.insert((tid, idx), val);
2937 Ok(())
2938 }
2939
2940 pub fn protected_call_raw(&mut self, func: StackIdx, nresults: i32, errfunc: StackIdx) -> Result<(), LuaError> {
2941 let ef = errfunc.0 as isize;
2942 let status = crate::do_::pcall(
2943 self,
2944 |s| s.call_no_yield(func, nresults),
2945 func,
2946 ef,
2947 );
2948 match status {
2949 LuaStatus::Ok => Ok(()),
2950 LuaStatus::ErrSyntax => {
2951 let err_val = self.get_at(func);
2952 self.set_top(func);
2953 Err(LuaError::Syntax(err_val))
2954 }
2955 LuaStatus::Yield => {
2956 self.set_top(func);
2957 Err(LuaError::Yield)
2958 }
2959 _ => {
2960 let err_val = self.get_at(func);
2961 self.set_top(func);
2962 Err(LuaError::Runtime(err_val))
2963 }
2964 }
2965 }
2966 pub fn protected_parser(&mut self, z: crate::zio::ZIO, name: &[u8], mode: Option<&[u8]>) -> LuaStatus {
2967 crate::do_::protected_parser(self, z, name, mode)
2968 }
2969 pub fn do_call(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2970 crate::do_::call(self, func, nresults)
2971 }
2972 pub fn do_call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2973 crate::do_::callnoyield(self, func, nresults)
2974 }
2975 pub fn call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2976 crate::do_::callnoyield(self, func, nresults)
2977 }
2978 pub fn call_at(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2979 crate::do_::call(self, func, nresults)
2980 }
2981 #[inline(always)]
2982 pub fn precall(&mut self, func: StackIdx, nresults: i32) -> Result<Option<CallInfoIdx>, LuaError> {
2983 crate::do_::precall(self, func, nresults)
2984 }
2985 #[inline(always)]
2986 pub fn pretailcall(
2987 &mut self,
2988 ci: CallInfoIdx,
2989 func: StackIdx,
2990 narg1: i32,
2991 delta: i32,
2992 ) -> Result<i32, LuaError> {
2993 crate::do_::pretailcall(self, ci, func, narg1, delta)
2994 }
2995 #[inline(always)]
2996 pub fn poscall<N: TryInto<i32>>(&mut self, ci: CallInfoIdx, nres: N) -> Result<(), LuaError>
2997 where
2998 <N as TryInto<i32>>::Error: std::fmt::Debug,
2999 {
3000 let n = nres.try_into().expect("poscall: nres out of i32 range");
3001 crate::do_::poscall(self, ci, n)
3002 }
3003 pub fn adjust_results(&mut self, nresults: i32) {
3004 const LUA_MULTRET: i32 = -1;
3005 if nresults <= LUA_MULTRET {
3006 let ci_idx = self.ci.as_usize();
3007 if self.call_info[ci_idx].top.0 < self.top.0 {
3008 self.call_info[ci_idx].top = self.top;
3009 }
3010 }
3011 }
3012 pub fn adjust_varargs(
3013 &mut self,
3014 ci: CallInfoIdx,
3015 nfixparams: i32,
3016 cl: &GcRef<lua_types::closure::LuaLClosure>,
3017 ) -> Result<(), LuaError> {
3018 crate::tagmethods::adjust_varargs(self, nfixparams, ci, &cl.0.proto)
3019 }
3020 pub fn get_varargs(
3021 &mut self,
3022 ci: CallInfoIdx,
3023 ra: StackIdx,
3024 n: i32,
3025 ) -> Result<i32, LuaError> {
3026 crate::tagmethods::get_varargs(self, ci, ra, n)?;
3027 Ok(0)
3028 }
3029
3030 pub fn close_upvals(&mut self, level: StackIdx) -> Result<(), LuaError> {
3031 crate::func::close_upval(self, level);
3032 Ok(())
3033 }
3034 pub fn close_upvals_status(&mut self, level: StackIdx, _status: i32) -> Result<(), LuaError> {
3035 crate::func::close_upval(self, level);
3036 Ok(())
3037 }
3038 pub fn close_upvals_from_base(&mut self, ci: CallInfoIdx) -> Result<(), LuaError> {
3039 let base = self.ci_base(ci);
3040 crate::func::close_upval(self, base);
3041 Ok(())
3042 }
3043
3044 pub fn arith_op(&mut self, op: i32, p1: &LuaValue, p2: &LuaValue) -> Result<LuaValue, LuaError> {
3045 let arith_op = match op {
3046 0 => lua_types::arith::ArithOp::Add,
3047 1 => lua_types::arith::ArithOp::Sub,
3048 2 => lua_types::arith::ArithOp::Mul,
3049 3 => lua_types::arith::ArithOp::Mod,
3050 4 => lua_types::arith::ArithOp::Pow,
3051 5 => lua_types::arith::ArithOp::Div,
3052 6 => lua_types::arith::ArithOp::Idiv,
3053 7 => lua_types::arith::ArithOp::Band,
3054 8 => lua_types::arith::ArithOp::Bor,
3055 9 => lua_types::arith::ArithOp::Bxor,
3056 10 => lua_types::arith::ArithOp::Shl,
3057 11 => lua_types::arith::ArithOp::Shr,
3058 12 => lua_types::arith::ArithOp::Unm,
3059 13 => lua_types::arith::ArithOp::Bnot,
3060 _ => return Err(LuaError::runtime(format_args!("invalid arith op {}", op))),
3061 };
3062 let mut res = LuaValue::Nil;
3063 if crate::object::raw_arith(self, arith_op, p1, p2, &mut res)? {
3064 Ok(res)
3065 } else {
3066 Err(LuaError::arith_error(p1, p2, "perform arithmetic on"))
3067 }
3068 }
3069 pub fn concat(&mut self, n: i32) -> Result<(), LuaError> {
3070 crate::vm::concat(self, n)
3071 }
3072 pub fn less_than(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3073 crate::vm::less_than(self, l, r)
3074 }
3075 pub fn less_equal(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3076 crate::vm::less_equal(self, l, r)
3077 }
3078 pub fn equal_obj(&self, _ctx: Option<&LuaValue>, l: &LuaValue, r: &LuaValue) -> bool {
3079 crate::vm::equal_obj(None, l, r).unwrap_or(false)
3080 }
3081 pub fn equal_obj_with_tm(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3082 crate::vm::equal_obj(Some(self), l, r)
3083 }
3084 pub fn obj_len(&mut self, v: &LuaValue) -> Result<LuaValue, LuaError> {
3085 match v {
3086 LuaValue::Table(_) => {
3087 let consult_len_tm =
3090 !matches!(self.global().lua_version, lua_types::LuaVersion::V51);
3091 let tm = if consult_len_tm {
3092 let mt = self.table_metatable(v);
3093 self.fast_tm_table(mt.as_ref(), TagMethod::Len)
3094 } else {
3095 LuaValue::Nil
3096 };
3097 if matches!(tm, LuaValue::Nil) {
3098 let n = self.table_length(v)?;
3099 return Ok(LuaValue::Int(n));
3100 }
3101 self.push(LuaValue::Nil);
3102 let slot = StackIdx(self.top.0 - 1);
3103 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3104 Ok(self.pop())
3105 }
3106 LuaValue::Str(s) => Ok(LuaValue::Int(s.len() as i64)),
3107 other => {
3108 let tm = crate::tagmethods::get_tm_by_obj(self, other, crate::tagmethods::TagMethod::Len);
3109 if matches!(tm, LuaValue::Nil) {
3110 let mut msg = b"attempt to get length of a ".to_vec();
3111 msg.extend_from_slice(&self.obj_type_name(other));
3112 msg.extend_from_slice(b" value");
3113 return Err(crate::debug::prefixed_runtime_pub(self, msg));
3114 }
3115 self.push(LuaValue::Nil);
3116 let slot = StackIdx(self.top.0 - 1);
3117 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3118 Ok(self.pop())
3119 }
3120 }
3121 }
3122 pub fn obj_to_string(&mut self, idx: i32) -> Result<GcRef<LuaString>, LuaError> {
3123 let slot: StackIdx = if idx > 0 {
3124 let ci_func = self.current_call_info().func;
3125 ci_func + idx
3126 } else {
3127 debug_assert!(idx != 0, "invalid index");
3128 StackIdx((self.top_idx().0 as i32 + idx) as u32)
3129 };
3130 let val = self.get_at(slot);
3131 match val {
3132 LuaValue::Str(s) => Ok(s),
3133 LuaValue::Int(_) | LuaValue::Float(_) => {
3134 let s = crate::object::num_to_string(self, &val)?;
3135 self.set_at(slot, LuaValue::Str(s.clone()));
3136 Ok(s)
3137 }
3138 _ => Err(LuaError::type_error(&val, "convert to string")),
3139 }
3140 }
3141 pub fn coerce_to_string(&mut self, idx: StackIdx) -> Result<GcRef<LuaString>, LuaError> {
3142 let val = self.get_at(idx);
3143 match val {
3144 LuaValue::Str(s) => Ok(s),
3145 LuaValue::Int(_) | LuaValue::Float(_) => {
3146 let s = crate::object::num_to_string(self, &val)?;
3147 self.set_at(idx, LuaValue::Str(s.clone()));
3148 Ok(s)
3149 }
3150 _ => Err(LuaError::type_error(&val, "convert to string")),
3151 }
3152 }
3153 pub fn str_to_num(&mut self, s: &[u8]) -> Option<(LuaValue, usize)> {
3154 let mut out = LuaValue::Nil;
3155 let sz = crate::object::str2num(s, &mut out);
3156 if sz == 0 { None } else { Some((out, sz)) }
3157 }
3158
3159 #[inline(always)]
3160 pub fn fast_get(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3161 let LuaValue::Table(tbl) = t else { return Ok(None); };
3162 let v = tbl.get(k);
3163 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3164 }
3165 #[inline(always)]
3166 pub fn fast_get_int(&mut self, t: &LuaValue, k: i64) -> Result<Option<LuaValue>, LuaError> {
3167 let LuaValue::Table(tbl) = t else { return Ok(None); };
3168 let v = tbl.get_int(k);
3169 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3170 }
3171 #[inline(always)]
3172 pub fn fast_get_short_str(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3173 let LuaValue::Table(tbl) = t else { return Ok(None); };
3174 let LuaValue::Str(s) = k else { return Ok(None); };
3175 let v = tbl.get_short_str(s);
3176 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3177 }
3178 #[inline(always)]
3179 pub fn fast_tm_table(&mut self, t: Option<&GcRef<LuaTable>>, tm: TagMethod) -> LuaValue {
3180 let Some(mt) = t else { return LuaValue::Nil; };
3181 debug_assert!((tm as u8) <= TagMethod::Eq as u8);
3182 let ename = self.global().tmname[tm as usize].clone();
3183 mt.get_short_str(&ename)
3184 }
3185 pub fn fast_tm_ud(&mut self, u: &GcRef<LuaUserData>, tm: TagMethod) -> LuaValue {
3186 let mt = u.metatable();
3188 self.fast_tm_table(mt.as_ref(), tm)
3189 }
3190
3191 pub fn table_get_with_tm(&mut self, t: &LuaValue, k: &LuaValue) -> Result<LuaValue, LuaError> {
3192 if let LuaValue::Table(tbl) = t {
3198 if tbl.metatable().is_none() {
3199 return Ok(tbl.get(k));
3200 }
3201 }
3202 if let Some(v) = self.fast_get(t, k)? {
3203 return Ok(v);
3204 }
3205 let res = self.top_idx();
3206 self.push(LuaValue::Nil);
3207 crate::vm::finish_get(self, t.clone(), k.clone(), res, true, None)?;
3208 let value = self.get_at(res);
3209 self.pop();
3210 Ok(value)
3211 }
3212 #[inline]
3227 pub fn table_set_with_tm(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3228 if let LuaValue::Table(tbl) = t {
3229 if tbl.metatable().is_none() {
3230 self.gc_table_barrier_back(tbl, &v);
3231 return self.table_raw_set(t, k, v);
3232 }
3233 }
3234 if self.fast_get(t, &k)?.is_some() {
3235 self.gc_value_barrier_back(t, &v);
3236 return self.table_raw_set(t, k, v);
3237 }
3238 crate::vm::finish_set(self, t.clone(), k, v, true, None, None)
3239 }
3240 #[inline]
3241 pub fn table_raw_set(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3242 let LuaValue::Table(tbl) = t else {
3243 return Err(LuaError::type_error(t, "index"));
3244 };
3245 let tbl = tbl.clone();
3246 tbl.raw_set(self, k, v)
3247 }
3248 #[inline]
3249 pub fn table_array_set(&mut self, t: &LuaValue, idx: usize, v: LuaValue) -> Result<(), LuaError> {
3250 let LuaValue::Table(tbl) = t else {
3251 return Err(LuaError::type_error(t, "index"));
3252 };
3253 let tbl = tbl.clone();
3254 tbl.raw_set_int(self, idx as i64 + 1, v)
3255 }
3256 pub fn table_ensure_array(&mut self, t: &LuaValue, n: usize) -> Result<(), LuaError> {
3257 let LuaValue::Table(tbl) = t else {
3258 return Err(LuaError::type_error(t, "index"));
3259 };
3260 if n > tbl.array_len() {
3261 tbl.resize(self, n, 0)?;
3262 }
3263 Ok(())
3264 }
3265 pub fn table_length(&mut self, t: &LuaValue) -> Result<i64, LuaError> {
3266 let LuaValue::Table(tbl) = t else {
3267 return Err(LuaError::type_error(t, "get length of"));
3268 };
3269 Ok(tbl.getn() as i64)
3270 }
3271 pub fn table_metatable(&mut self, v: &LuaValue) -> Option<GcRef<LuaTable>> {
3272 match v {
3273 LuaValue::Table(t) => t.metatable(),
3274 LuaValue::UserData(u) => u.metatable(),
3275 other => {
3276 let idx = other.base_type() as usize;
3277 self.global().mt[idx].clone()
3278 }
3279 }
3280 }
3281 pub fn table_resize(&mut self, t: &GcRef<LuaTable>, na: usize, nh: usize) -> Result<(), LuaError> {
3282 self.mark_gc_check_needed();
3283 t.resize(self, na, nh)
3284 }
3285 pub fn table_getn(&self, t: &GcRef<LuaTable>) -> i64 {
3286 let mut i: i64 = 1;
3294 loop {
3295 let v = t.get_int(i);
3296 if matches!(v, LuaValue::Nil) {
3297 return i - 1;
3298 }
3299 i += 1;
3300 }
3301 }
3302
3303 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> {
3304 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3305 crate::tagmethods::try_bin_tm(self, p1, p1_idx, p2, p2_idx, res, event)
3306 }
3307 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> {
3308 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3309 crate::tagmethods::try_bini_tm(self, p1, p1_idx, imm, flip, res, event)
3310 }
3311 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> {
3312 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3313 crate::tagmethods::try_bin_assoc_tm(self, p1, p1_idx, p2, p2_idx, flip, res, event)
3314 }
3315 pub fn try_concat_tm(&mut self, _p1: &LuaValue, _p2: &LuaValue) -> Result<(), LuaError> {
3316 crate::tagmethods::try_concat_tm(self)
3317 }
3318 pub fn call_tm(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, p3: &LuaValue) -> Result<(), LuaError> {
3319 crate::tagmethods::call_tm(self, f, p1.clone(), p2.clone(), p3.clone())
3320 }
3321 pub fn call_tm_res(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, res: StackIdx) -> Result<(), LuaError> {
3322 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)
3323 }
3324 pub fn call_tm_res_bool(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue) -> Result<bool, LuaError> {
3325 let res = self.top_idx();
3326 self.push(LuaValue::Nil);
3327 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)?;
3328 let result = self.get_at(res).clone();
3329 self.pop();
3330 Ok(!matches!(result, LuaValue::Nil | LuaValue::Bool(false)))
3331 }
3332 pub fn call_order_tm(&mut self, p1: &LuaValue, p2: &LuaValue, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3333 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3334 crate::tagmethods::call_order_tm(self, p1, p2, event)
3335 }
3336 pub fn call_order_i_tm(&mut self, p1: &LuaValue, v2: i64, flip: bool, isfloat: bool, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3337 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3338 crate::tagmethods::call_orderi_tm(self, p1, v2 as i32, flip, isfloat, event)
3339 }
3340
3341 #[inline(always)]
3342 pub fn proto_code(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, pc: u32) -> lua_types::opcode::Instruction {
3343 cl.proto.code[pc as usize]
3344 }
3345 #[inline(always)]
3346 pub fn proto_const(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> LuaValue {
3347 cl.proto.k[idx].clone()
3348 }
3349 #[inline(always)]
3355 pub fn proto_const_int(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<i64> {
3356 match &cl.proto.k[idx] {
3357 LuaValue::Int(v) => Some(*v),
3358 _ => None,
3359 }
3360 }
3361 #[inline(always)]
3365 pub fn proto_const_num(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<f64> {
3366 match &cl.proto.k[idx] {
3367 LuaValue::Float(f) => Some(*f),
3368 LuaValue::Int(v) => Some(*v as f64),
3369 _ => None,
3370 }
3371 }
3372 pub fn get_proto_instr(&self, ci: CallInfoIdx, pc: u32) -> lua_types::opcode::Instruction {
3373 let cl = self.ci_lua_closure(ci)
3374 .expect("get_proto_instr: CallInfo does not hold a Lua closure");
3375 cl.proto.code[pc as usize]
3376 }
3377 pub fn trace_call(&mut self, _idx: CallInfoIdx) -> Result<bool, LuaError> {
3382 Ok(crate::debug::trace_call(self)? != 0)
3383 }
3384 pub fn trace_exec(&mut self, _idx: CallInfoIdx, pc: u32) -> Result<bool, LuaError> {
3387 Ok(crate::debug::trace_exec(self, pc)? != 0)
3388 }
3389 pub fn hook_call(&mut self, idx: CallInfoIdx) -> Result<(), LuaError> {
3390 crate::do_::hookcall(self, idx)
3391 }
3392 #[inline(always)]
3393 fn gc_step_flags(&self) -> Option<(bool, bool)> {
3394 let g = self.global();
3395 if !g.is_gc_running() {
3396 return None;
3397 }
3398 let should_collect = g.heap.would_collect();
3399 let has_finalizers = g.finalizers.has_to_be_finalized();
3400 if should_collect || has_finalizers {
3401 Some((should_collect, has_finalizers))
3402 } else {
3403 None
3404 }
3405 }
3406
3407 #[inline(always)]
3408 fn should_check_gc(&mut self) -> bool {
3409 if self.gc_check_needed {
3410 return true;
3411 }
3412 if self.global().finalizers.has_to_be_finalized() {
3413 self.gc_check_needed = true;
3414 return true;
3415 }
3416 false
3417 }
3418
3419 #[inline(always)]
3420 pub(crate) fn mark_gc_check_needed(&mut self) {
3421 self.gc_check_needed = true;
3422 }
3423
3424 #[inline(always)]
3425 pub fn gc_check_step(&mut self) {
3426 if !self.allowhook {
3427 return;
3428 }
3429 if !self.should_check_gc() {
3430 return;
3431 }
3432 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3433 self.gc_check_needed = false;
3434 return;
3435 };
3436 if should_collect || has_finalizers {
3437 if should_collect {
3438 self.gc().check_step();
3439 }
3440 crate::api::run_pending_finalizers(self);
3441 self.gc_check_needed = true;
3442 }
3443 let should_keep_checking = {
3444 let g = self.global();
3445 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3446 };
3447 self.gc_check_needed = should_keep_checking;
3448 }
3449 #[inline(always)]
3450 pub fn gc_cond_step(&mut self) {
3451 if !self.allowhook {
3452 return;
3453 }
3454 if !self.should_check_gc() {
3455 return;
3456 }
3457 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3458 self.gc_check_needed = false;
3459 return;
3460 };
3461 if should_collect || has_finalizers {
3462 if should_collect {
3463 self.gc().check_step();
3464 }
3465 crate::api::run_pending_finalizers(self);
3466 self.gc_check_needed = true;
3467 }
3468 let should_keep_checking = {
3469 let g = self.global();
3470 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3471 };
3472 self.gc_check_needed = should_keep_checking;
3473 }
3474 pub fn gc_barrier_back(&mut self, t: &dyn std::any::Any, v: &LuaValue) {
3475 self.gc().barrier_back(t, v);
3476 }
3477 pub fn gc_value_barrier_back(&mut self, t: &LuaValue, v: &LuaValue) {
3478 if let LuaValue::Table(tbl) = t {
3479 self.gc_table_barrier_back(tbl, v);
3480 } else {
3481 self.gc_barrier_back(t, v);
3482 }
3483 }
3484 pub fn gc_table_barrier_back(&mut self, t: &GcRef<LuaTable>, v: &LuaValue) {
3485 self.gc().table_barrier_back(t, v);
3486 }
3487 pub fn gc_barrier_upval(&mut self, uv: &GcRef<UpVal>, v: &LuaValue) {
3488 self.gc().barrier(uv, v);
3489 }
3490 pub fn is_main_thread(&mut self) -> bool {
3496 let g = self.global();
3497 g.current_thread_id == g.main_thread_id
3498 }
3499 pub fn obj_type_name<'v>(&self, v: &'v LuaValue) -> std::borrow::Cow<'static, [u8]> {
3500 match v {
3501 LuaValue::LightUserData(_) => std::borrow::Cow::Borrowed(b"light userdata"),
3502 LuaValue::Table(t) => {
3503 if let Some(mt) = t.metatable() {
3504 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3505 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3506 }
3507 }
3508 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3509 }
3510 LuaValue::UserData(u) => {
3511 if let Some(mt) = u.metatable() {
3512 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3513 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3514 }
3515 }
3516 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3517 }
3518 _ => std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type())),
3519 }
3520 }
3521
3522 pub fn full_type_name(&mut self, v: &LuaValue) -> Result<Vec<u8>, LuaError> {
3523 crate::tagmethods::obj_type_name(self, v)
3524 }
3525 pub fn emit_warning(&mut self, _msg: &[u8], _to_cont: bool) { warning(self, _msg, _to_cont) }
3526}
3527
3528pub struct GcHandle<'a> {
3534 _state: &'a mut LuaState,
3535}
3536
3537struct CollectRoots<'a> {
3544 global: &'a GlobalState,
3545 thread: &'a LuaState,
3546}
3547
3548#[derive(Clone, Copy)]
3549enum HeapCollectMode {
3550 Full,
3551 Step,
3552 Minor,
3553}
3554
3555impl<'a> lua_gc::Trace for CollectRoots<'a> {
3556 fn trace(&self, m: &mut lua_gc::Marker) {
3557 self.global.trace(m);
3558 self.thread.trace(m);
3559 }
3560}
3561
3562#[derive(Clone, Copy)]
3563enum BarrierKind {
3564 Forward,
3565 Backward,
3566}
3567
3568fn barrier_lua_value<P>(
3569 heap: &lua_gc::Heap,
3570 parent: GcRef<P>,
3571 child: &LuaValue,
3572 generational: bool,
3573 kind: BarrierKind,
3574)
3575where
3576 P: lua_gc::Trace + 'static,
3577{
3578 if generational && matches!(kind, BarrierKind::Backward) {
3579 heap.generational_backward_barrier(parent.0);
3580 }
3581 match child {
3582 LuaValue::Str(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3583 LuaValue::Table(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3584 LuaValue::Function(LuaClosure::Lua(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3585 LuaValue::Function(LuaClosure::C(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3586 LuaValue::UserData(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3587 LuaValue::Thread(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3588 LuaValue::Nil
3589 | LuaValue::Bool(_)
3590 | LuaValue::Int(_)
3591 | LuaValue::Float(_)
3592 | LuaValue::LightUserData(_)
3593 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3594 }
3595}
3596
3597fn barrier_gc_child<P, C>(
3598 heap: &lua_gc::Heap,
3599 parent: GcRef<P>,
3600 child: GcRef<C>,
3601 generational: bool,
3602 kind: BarrierKind,
3603)
3604where
3605 P: lua_gc::Trace + 'static,
3606 C: lua_gc::Trace + 'static,
3607{
3608 if generational && matches!(kind, BarrierKind::Forward) {
3609 heap.generational_forward_barrier(parent.0, child.0);
3610 } else if matches!(kind, BarrierKind::Backward) {
3611 heap.barrier_back(parent.0, child.0);
3612 } else {
3613 heap.barrier(parent.0, child.0);
3614 }
3615}
3616
3617fn barrier_child_any<P>(
3618 heap: &lua_gc::Heap,
3619 parent: GcRef<P>,
3620 child: &dyn std::any::Any,
3621 generational: bool,
3622 kind: BarrierKind,
3623)
3624where
3625 P: lua_gc::Trace + 'static,
3626{
3627 if let Some(v) = child.downcast_ref::<LuaValue>() {
3628 barrier_lua_value(heap, parent, v, generational, kind);
3629 } else if let Some(c) = child.downcast_ref::<GcRef<LuaString>>() {
3630 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3631 } else if let Some(c) = child.downcast_ref::<GcRef<LuaTable>>() {
3632 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3633 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureLua>>() {
3634 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3635 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureC>>() {
3636 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3637 } else if let Some(c) = child.downcast_ref::<GcRef<LuaUserData>>() {
3638 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3639 } else if let Some(c) = child.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3640 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3641 } else if let Some(c) = child.downcast_ref::<GcRef<LuaProto>>() {
3642 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3643 } else if let Some(c) = child.downcast_ref::<GcRef<UpVal>>() {
3644 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3645 }
3646}
3647
3648fn barrier_any(
3649 heap: &lua_gc::Heap,
3650 parent: &dyn std::any::Any,
3651 child: &dyn std::any::Any,
3652 generational: bool,
3653 kind: BarrierKind,
3654) {
3655 if let Some(v) = parent.downcast_ref::<LuaValue>() {
3656 match v {
3657 LuaValue::Str(p) => barrier_child_any(heap, *p, child, generational, kind),
3658 LuaValue::Table(p) => barrier_child_any(heap, *p, child, generational, kind),
3659 LuaValue::Function(LuaClosure::Lua(p)) => barrier_child_any(heap, *p, child, generational, kind),
3660 LuaValue::Function(LuaClosure::C(p)) => barrier_child_any(heap, *p, child, generational, kind),
3661 LuaValue::UserData(p) => barrier_child_any(heap, *p, child, generational, kind),
3662 LuaValue::Thread(p) => barrier_child_any(heap, *p, child, generational, kind),
3663 LuaValue::Nil
3664 | LuaValue::Bool(_)
3665 | LuaValue::Int(_)
3666 | LuaValue::Float(_)
3667 | LuaValue::LightUserData(_)
3668 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3669 }
3670 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaString>>() {
3671 barrier_child_any(heap, p.clone(), child, generational, kind);
3672 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaTable>>() {
3673 barrier_child_any(heap, p.clone(), child, generational, kind);
3674 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureLua>>() {
3675 barrier_child_any(heap, p.clone(), child, generational, kind);
3676 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureC>>() {
3677 barrier_child_any(heap, p.clone(), child, generational, kind);
3678 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaUserData>>() {
3679 barrier_child_any(heap, p.clone(), child, generational, kind);
3680 } else if let Some(p) = parent.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3681 barrier_child_any(heap, p.clone(), child, generational, kind);
3682 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaProto>>() {
3683 barrier_child_any(heap, p.clone(), child, generational, kind);
3684 } else if let Some(p) = parent.downcast_ref::<GcRef<UpVal>>() {
3685 barrier_child_any(heap, p.clone(), child, generational, kind);
3686 }
3687}
3688
3689fn trace_reachable_threads(
3690 global: &GlobalState,
3691 _current_thread_id: u64,
3692 marker: &mut lua_gc::Marker,
3693) {
3694 use lua_gc::Trace;
3695
3696 loop {
3697 let visited_before = marker.visited_count();
3698 for (id, entry) in global.threads.iter() {
3699 if thread_entry_marked_alive(marker, *id, entry) {
3700 if let Ok(thread) = entry.state.try_borrow() {
3701 thread.trace(marker);
3702 }
3703 }
3704 }
3705 marker.drain_gray_queue();
3706 if marker.visited_count() == visited_before {
3707 break;
3708 }
3709 }
3710}
3711
3712fn thread_entry_marked_alive(
3713 marker: &lua_gc::Marker,
3714 id: u64,
3715 entry: &ThreadRegistryEntry,
3716) -> bool {
3717 marker.is_marked_or_old(entry.value.0) && entry.value.id == id
3718}
3719
3720fn lua_value_marked_or_old(marker: &lua_gc::Marker, value: &LuaValue) -> bool {
3721 match value {
3722 LuaValue::Str(v) => marker.is_marked_or_old(v.0),
3723 LuaValue::Table(v) => marker.is_marked_or_old(v.0),
3724 LuaValue::Function(LuaClosure::Lua(v)) => marker.is_marked_or_old(v.0),
3725 LuaValue::Function(LuaClosure::C(v)) => marker.is_marked_or_old(v.0),
3726 LuaValue::UserData(v) => marker.is_marked_or_old(v.0),
3727 LuaValue::Thread(v) => marker.is_marked_or_old(v.0),
3728 LuaValue::Nil
3729 | LuaValue::Bool(_)
3730 | LuaValue::Int(_)
3731 | LuaValue::Float(_)
3732 | LuaValue::LightUserData(_)
3733 | LuaValue::Function(LuaClosure::LightC(_)) => true,
3734 }
3735}
3736
3737fn lua_value_identity(value: &LuaValue) -> Option<usize> {
3738 match value {
3739 LuaValue::Str(v) => Some(v.identity()),
3740 LuaValue::Table(v) => Some(v.identity()),
3741 LuaValue::Function(LuaClosure::Lua(v)) => Some(v.identity()),
3742 LuaValue::Function(LuaClosure::C(v)) => Some(v.identity()),
3743 LuaValue::UserData(v) => Some(v.identity()),
3744 LuaValue::Thread(v) => Some(v.identity()),
3745 LuaValue::Nil
3746 | LuaValue::Bool(_)
3747 | LuaValue::Int(_)
3748 | LuaValue::Float(_)
3749 | LuaValue::LightUserData(_)
3750 | LuaValue::Function(LuaClosure::LightC(_)) => None,
3751 }
3752}
3753
3754fn finalizer_marked_or_old(marker: &lua_gc::Marker, object: &FinalizerObject) -> bool {
3755 match object {
3756 FinalizerObject::Table(t) => marker.is_marked_or_old(t.0),
3757 FinalizerObject::UserData(u) => marker.is_marked_or_old(u.0),
3758 }
3759}
3760
3761fn weak_snapshot_tables<'a>(
3762 snapshot: &'a lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>>,
3763) -> impl Iterator<Item = &'a GcRef<LuaTable>> {
3764 snapshot
3765 .weak_values
3766 .iter()
3767 .chain(snapshot.ephemeron.iter())
3768 .chain(snapshot.all_weak.iter())
3769}
3770
3771fn close_open_upvalues_for_unreachable_threads(
3772 global: &GlobalState,
3773 marker: &mut lua_gc::Marker,
3774) {
3775 use lua_gc::Trace;
3776
3777 let mut closed_values = Vec::<LuaValue>::new();
3778 for (id, entry) in global.threads.iter() {
3779 if entry.value.id != *id {
3780 continue;
3781 }
3782 if thread_entry_marked_alive(marker, *id, entry) {
3783 continue;
3784 }
3785 let Ok(thread) = entry.state.try_borrow() else {
3786 continue;
3787 };
3788 for uv in thread.openupval.iter() {
3789 if !marker.is_visited(uv.identity()) {
3790 continue;
3791 }
3792 let Some((thread_id, idx)) = uv.try_open_payload() else {
3793 continue;
3794 };
3795 if thread_id as u64 != *id {
3796 continue;
3797 }
3798 let value = thread.get_at(idx);
3799 uv.close_with(value.clone());
3800 closed_values.push(value);
3801 }
3802 }
3803 for value in closed_values {
3804 value.trace(marker);
3805 }
3806 marker.drain_gray_queue();
3807}
3808
3809fn record_live_interned_strings(
3810 global: &GlobalState,
3811 marker: &lua_gc::Marker,
3812 live_ids: &std::cell::RefCell<Vec<usize>>,
3813) {
3814 let mut live = live_ids.borrow_mut();
3815 for s in global.interned_lt.values() {
3816 let id = s.identity();
3817 if marker.is_visited(id) {
3818 live.push(id);
3819 }
3820 }
3821}
3822
3823fn retain_live_interned_strings(
3824 global: &mut GlobalState,
3825 mut live_ids: Vec<usize>,
3826) {
3827 if live_ids.is_empty() {
3828 global.interned_lt.clear();
3829 return;
3830 }
3831 live_ids.sort_unstable();
3832 live_ids.dedup();
3833 global
3834 .interned_lt
3835 .retain(|_, s| live_ids.binary_search(&s.identity()).is_ok());
3836}
3837
3838impl<'a> GcHandle<'a> {
3839 pub fn check_step(&self) {
3846 if !self._state.global().is_gc_running() {
3847 return;
3848 }
3849 if self._state.global().is_gen_mode() {
3850 let should_collect = {
3851 let g = self._state.global();
3852 g.heap.would_collect() || g.gc_debt() > 0
3853 };
3854 if should_collect {
3855 self.generational_step();
3856 }
3857 } else {
3858 self.collect_via_heap(false);
3859 }
3860 }
3861
3862 pub fn full_collect(&self) {
3864 if self._state.global().is_gen_mode() {
3865 self.fullgen();
3866 } else {
3867 self.collect_via_heap(true);
3868 }
3869 }
3870
3871 fn negative_debt(bytes: usize) -> isize {
3872 -(bytes.min(isize::MAX as usize) as isize)
3873 }
3874
3875 fn set_minor_debt(&self) {
3876 let mut g = self._state.global_mut();
3877 let total = g.total_bytes();
3878 let growth = (total / 100).saturating_mul(g.genminormul as usize);
3879 g.heap
3880 .set_threshold_bytes(total.saturating_add(growth.max(1)));
3881 set_debt(&mut *g, Self::negative_debt(growth));
3882 }
3883
3884 fn set_pause_debt(&self) {
3885 let mut g = self._state.global_mut();
3886 let total = g.total_bytes();
3887 let pause = g.gc_pause_param().max(0) as usize;
3888 let threshold = g.gc_estimate.max(1).saturating_mul(pause) / 100;
3889 let debt = if threshold > total {
3890 Self::negative_debt(threshold - total)
3891 } else {
3892 0
3893 };
3894 let heap_threshold = if threshold > total {
3895 threshold
3896 } else {
3897 total.saturating_add(1)
3898 };
3899 g.heap.set_threshold_bytes(heap_threshold);
3900 set_debt(&mut *g, debt);
3901 }
3902
3903 fn enter_incremental_mode(&self) {
3904 let mut g = self._state.global_mut();
3905 g.heap.reset_all_ages();
3906 g.finalizers.reset_generation_boundaries();
3907 g.gckind = GcKind::Incremental as u8;
3908 g.lastatomic = 0;
3909 }
3910
3911 fn enter_generational_mode(&self) -> usize {
3912 self.collect_via_heap_mode(HeapCollectMode::Full);
3913 let numobjs = {
3914 let mut g = self._state.global_mut();
3915 g.heap.promote_all_to_old();
3916 g.finalizers.promote_all_pending_to_old();
3917 g.heap.allgc_count()
3918 };
3919 let total = self._state.global().total_bytes();
3920 {
3921 let mut g = self._state.global_mut();
3922 g.gckind = GcKind::Generational as u8;
3923 g.lastatomic = 0;
3924 g.gc_estimate = total;
3925 }
3926 self.set_minor_debt();
3927 numobjs
3928 }
3929
3930 fn fullgen(&self) -> usize {
3931 self.enter_incremental_mode();
3932 self.enter_generational_mode()
3933 }
3934
3935 fn stepgenfull(&self, lastatomic: usize) {
3936 if self._state.global().gckind == GcKind::Generational as u8 {
3937 self.enter_incremental_mode();
3938 }
3939 self.collect_via_heap_mode(HeapCollectMode::Full);
3940 let newatomic = self._state.global().heap.allgc_count().max(1);
3941 if newatomic < lastatomic.saturating_add(lastatomic >> 3) {
3942 {
3943 let mut g = self._state.global_mut();
3944 g.heap.promote_all_to_old();
3945 g.finalizers.promote_all_pending_to_old();
3946 }
3947 let total = self._state.global().total_bytes();
3948 {
3949 let mut g = self._state.global_mut();
3950 g.gckind = GcKind::Generational as u8;
3951 g.lastatomic = 0;
3952 g.gc_estimate = total;
3953 }
3954 self.set_minor_debt();
3955 } else {
3956 {
3957 let mut g = self._state.global_mut();
3958 g.heap.reset_all_ages();
3959 g.finalizers.reset_generation_boundaries();
3960 }
3961 let total = self._state.global().total_bytes();
3962 {
3963 let mut g = self._state.global_mut();
3964 g.gckind = GcKind::Incremental as u8;
3965 g.lastatomic = newatomic;
3966 g.gc_estimate = total;
3967 }
3968 self.set_pause_debt();
3969 }
3970 }
3971
3972 fn collect_via_heap(&self, force: bool) {
3981 self.collect_via_heap_mode(if force {
3982 HeapCollectMode::Full
3983 } else {
3984 HeapCollectMode::Step
3985 });
3986 }
3987
3988 fn collect_via_heap_mode(&self, mode: HeapCollectMode) {
3989 use lua_gc::Trace;
3990 let state_ref: &LuaState = &*self._state;
3991
3992 if matches!(mode, HeapCollectMode::Step) {
3998 let g = state_ref.global.borrow();
3999 if !g.heap.would_collect() {
4000 return;
4001 }
4002 }
4003
4004 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4008 let mut g = state_ref.global.borrow_mut();
4009 g.weak_tables_registry.live_snapshot_by_kind()
4010 };
4011
4012 let weak_table_capacity = weak_tables_snapshot.len();
4017 let (pending_snapshot, thread_capacity, interned_capacity): (
4018 Vec<FinalizerObject>,
4019 usize,
4020 usize,
4021 ) = {
4022 let g = state_ref.global.borrow();
4023 let pending = match mode {
4024 HeapCollectMode::Minor => g.finalizers.pending_minor_snapshot(),
4025 HeapCollectMode::Full | HeapCollectMode::Step => g.finalizers.pending_snapshot(),
4026 };
4027 (pending, g.threads.len(), g.interned_lt.len())
4028 };
4029 let finalizer_capacity = pending_snapshot.len();
4030
4031 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4032 std::cell::RefCell::new(std::collections::HashSet::new());
4033 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4034 std::cell::RefCell::new(Vec::new());
4035 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4036 std::cell::RefCell::new(std::collections::HashSet::new());
4037 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4038 std::cell::RefCell::new(std::collections::HashSet::new());
4039 let live_interned_ids: std::cell::RefCell<Vec<usize>> =
4040 std::cell::RefCell::new(Vec::new());
4041 let collect_ran = std::cell::Cell::new(false);
4042
4043 {
4044 let global = state_ref.global.borrow();
4045 global.heap.unpause();
4046 let roots = CollectRoots { global: &*global, thread: state_ref };
4047 let hook = |marker: &mut lua_gc::Marker| {
4048 collect_ran.set(true);
4049 alive_ids.borrow_mut().reserve(weak_table_capacity);
4050 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4051 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4052 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4053 live_interned_ids.borrow_mut().reserve(interned_capacity);
4054 trace_reachable_threads(&*global, global.current_thread_id, marker);
4055 close_open_upvalues_for_unreachable_threads(&*global, marker);
4056 loop {
4057 let visited_before = marker.visited_count();
4058 for t in &weak_tables_snapshot.ephemeron {
4059 if !marker.is_marked_or_old(t.0) {
4060 continue;
4061 }
4062 let to_mark = t.ephemeron_values_to_mark_with_value(
4063 &|v| lua_value_marked_or_old(marker, v),
4064 );
4065 for v in &to_mark {
4066 v.trace(marker);
4067 }
4068 }
4069 marker.drain_gray_queue();
4070 if marker.visited_count() == visited_before {
4071 break;
4072 }
4073 }
4074 for pf in &pending_snapshot {
4075 if !finalizer_marked_or_old(marker, pf) {
4076 pf.mark(marker);
4077 finalizing_ids.borrow_mut().insert(pf.identity());
4078 newly_unreachable.borrow_mut().push(pf.clone());
4079 }
4080 }
4081 marker.drain_gray_queue();
4082 loop {
4083 let visited_before = marker.visited_count();
4084 for t in &weak_tables_snapshot.ephemeron {
4085 if !marker.is_marked_or_old(t.0) {
4086 continue;
4087 }
4088 let to_mark = t.ephemeron_values_to_mark_with_value(
4089 &|v| lua_value_marked_or_old(marker, v),
4090 );
4091 for v in &to_mark {
4092 v.trace(marker);
4093 }
4094 }
4095 marker.drain_gray_queue();
4096 if marker.visited_count() == visited_before {
4097 break;
4098 }
4099 }
4100 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4101 let id = t.identity();
4102 if marker.is_marked_or_old(t.0) {
4103 let to_mark = {
4104 let finalizing = finalizing_ids.borrow();
4105 t.prune_weak_dead_with_value(
4106 &|v| lua_value_marked_or_old(marker, v),
4107 &|v| {
4108 lua_value_marked_or_old(marker, v)
4109 && lua_value_identity(v)
4110 .map_or(true, |id| !finalizing.contains(&id))
4111 },
4112 )
4113 };
4114 for v in &to_mark {
4115 v.trace(marker);
4116 }
4117 alive_ids.borrow_mut().insert(id);
4118 }
4119 }
4120 marker.drain_gray_queue();
4121 {
4122 let mut alive = alive_thread_ids.borrow_mut();
4123 for (id, entry) in global.threads.iter() {
4124 if thread_entry_marked_alive(marker, *id, entry) {
4125 alive.insert(*id);
4126 }
4127 }
4128 }
4129 record_live_interned_strings(&*global, marker, &live_interned_ids);
4130 };
4131 match mode {
4132 HeapCollectMode::Full => global.heap.full_collect_with_post_mark(&roots, hook),
4133 HeapCollectMode::Step => global.heap.step_with_post_mark(&roots, hook),
4134 HeapCollectMode::Minor => global.heap.minor_collect_with_post_mark(&roots, hook),
4135 }
4136 }
4137
4138 if !collect_ran.get() {
4139 return;
4140 }
4141
4142 let alive_set = alive_ids.into_inner();
4146 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4147 let alive_thread_ids = alive_thread_ids.into_inner();
4148 let live_interned_ids = live_interned_ids.into_inner();
4149 let mut g = state_ref.global.borrow_mut();
4150 retain_live_interned_strings(&mut *g, live_interned_ids);
4151 g.weak_tables_registry.retain_identities(&alive_set);
4152 let main_thread_id = g.main_thread_id;
4153 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4154 g.cross_thread_upvals
4155 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4156 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4160 for object in &promoted {
4161 if let Some(ptr) = object.heap_ptr() {
4162 g.heap.move_finobj_to_tobefnz(ptr);
4163 }
4164 }
4165 if matches!(mode, HeapCollectMode::Minor) {
4166 g.finalizers.finish_minor_collection();
4167 }
4168 }
4169
4170 pub fn generational_step(&self) -> bool {
4172 self.generational_step_with_major(true)
4173 }
4174
4175 pub fn generational_step_minor_only(&self) -> bool {
4181 self.generational_step_with_major(false)
4182 }
4183
4184 fn generational_step_with_major(&self, allow_major: bool) -> bool {
4185 let (lastatomic, majorbase, majorinc, should_major) = {
4186 let g = self._state.global();
4187 let majorbase = if g.gc_estimate == 0 {
4188 g.total_bytes()
4189 } else {
4190 g.gc_estimate
4191 };
4192 let majormul = g.gc_genmajormul_param().max(0) as usize;
4193 let majorinc = (majorbase / 100).saturating_mul(majormul);
4194 let debt_due = g.gc_debt() > 0 || g.heap.would_collect();
4195 let should_major = allow_major
4196 && debt_due
4197 && g.total_bytes() > majorbase.saturating_add(majorinc);
4198 (g.lastatomic, majorbase, majorinc, should_major)
4199 };
4200
4201 if lastatomic != 0 {
4202 self.stepgenfull(lastatomic);
4203 debug_assert!(self._state.global().is_gen_mode());
4204 return true;
4205 }
4206
4207 if should_major {
4208 let numobjs = self.fullgen();
4209 let after = self._state.global().total_bytes();
4210 if after < majorbase.saturating_add(majorinc / 2) {
4211 self.set_minor_debt();
4212 } else {
4213 {
4214 let mut g = self._state.global_mut();
4215 g.lastatomic = numobjs.max(1);
4216 }
4217 self.set_pause_debt();
4218 }
4219 } else {
4220 self.collect_via_heap_mode(HeapCollectMode::Minor);
4221 self.set_minor_debt();
4222 self._state.global_mut().gc_estimate = majorbase;
4223 }
4224
4225 debug_assert!(self._state.global().is_gen_mode());
4226 true
4227 }
4228
4229 pub fn step(&self) { }
4231
4232 pub fn incremental_step(&self, work_units: isize) -> bool {
4245 self.incremental_step_to_state(work_units, None)
4246 }
4247
4248 pub fn run_until_gc_state_for_test(&self, target: lua_gc::GcState) -> bool {
4253 self.incremental_step_to_state(isize::MAX / 4, Some(target));
4254 self._state.global().heap.gc_state() == target
4255 }
4256
4257 fn incremental_step_to_state(
4258 &self,
4259 work_units: isize,
4260 target: Option<lua_gc::GcState>,
4261 ) -> bool {
4262 use lua_gc::{StepBudget, StepOutcome, Trace};
4263 let state_ref: &LuaState = &*self._state;
4264
4265 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4266 let mut g = state_ref.global.borrow_mut();
4267 g.weak_tables_registry.live_snapshot_by_kind()
4268 };
4269
4270 let weak_table_capacity = weak_tables_snapshot.len();
4271 let (pending_snapshot, thread_capacity, interned_capacity): (
4272 Vec<FinalizerObject>,
4273 usize,
4274 usize,
4275 ) = {
4276 let g = state_ref.global.borrow();
4277 (g.finalizers.pending_snapshot(), g.threads.len(), g.interned_lt.len())
4278 };
4279 let finalizer_capacity = pending_snapshot.len();
4280
4281 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4282 std::cell::RefCell::new(std::collections::HashSet::new());
4283 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4284 std::cell::RefCell::new(Vec::new());
4285 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4286 std::cell::RefCell::new(std::collections::HashSet::new());
4287 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4288 std::cell::RefCell::new(std::collections::HashSet::new());
4289 let live_interned_ids: std::cell::RefCell<Vec<usize>> =
4290 std::cell::RefCell::new(Vec::new());
4291 let atomic_ran = std::cell::Cell::new(false);
4292
4293 let stop_target = {
4294 let g = state_ref.global.borrow();
4295 match (target, g.heap.gc_state()) {
4296 (Some(target), _) => Some(target),
4297 (None, lua_gc::GcState::CallFin) => None,
4298 (None, _) => Some(lua_gc::GcState::CallFin),
4299 }
4300 };
4301
4302 let outcome = {
4303 let global = state_ref.global.borrow();
4304 global.heap.unpause();
4305 let roots = CollectRoots { global: &*global, thread: state_ref };
4306 let hook = |marker: &mut lua_gc::Marker| {
4307 atomic_ran.set(true);
4308 alive_ids.borrow_mut().reserve(weak_table_capacity);
4309 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4310 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4311 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4312 live_interned_ids.borrow_mut().reserve(interned_capacity);
4313 trace_reachable_threads(&*global, global.current_thread_id, marker);
4314 close_open_upvalues_for_unreachable_threads(&*global, marker);
4315 loop {
4316 let visited_before = marker.visited_count();
4317 for t in &weak_tables_snapshot.ephemeron {
4318 let t_id = t.identity();
4319 if !marker.is_visited(t_id) {
4320 continue;
4321 }
4322 let to_mark = t.ephemeron_values_to_mark(
4323 &|id| marker.is_visited(id),
4324 );
4325 for v in &to_mark {
4326 v.trace(marker);
4327 }
4328 }
4329 marker.drain_gray_queue();
4330 if marker.visited_count() == visited_before {
4331 break;
4332 }
4333 }
4334 for pf in &pending_snapshot {
4335 if !marker.is_visited(pf.identity()) {
4336 pf.mark(marker);
4337 finalizing_ids.borrow_mut().insert(pf.identity());
4338 newly_unreachable.borrow_mut().push(pf.clone());
4339 }
4340 }
4341 marker.drain_gray_queue();
4342 loop {
4343 let visited_before = marker.visited_count();
4344 for t in &weak_tables_snapshot.ephemeron {
4345 let t_id = t.identity();
4346 if !marker.is_visited(t_id) {
4347 continue;
4348 }
4349 let to_mark = t.ephemeron_values_to_mark(
4350 &|id| marker.is_visited(id),
4351 );
4352 for v in &to_mark {
4353 v.trace(marker);
4354 }
4355 }
4356 marker.drain_gray_queue();
4357 if marker.visited_count() == visited_before {
4358 break;
4359 }
4360 }
4361 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4362 let id = t.identity();
4363 if marker.is_visited(id) {
4364 let to_mark = {
4365 let finalizing = finalizing_ids.borrow();
4366 t.prune_weak_dead_with(
4367 &|id| marker.is_visited(id),
4368 &|id| marker.is_visited(id) && !finalizing.contains(&id),
4369 )
4370 };
4371 for v in &to_mark {
4372 v.trace(marker);
4373 }
4374 alive_ids.borrow_mut().insert(id);
4375 }
4376 }
4377 marker.drain_gray_queue();
4378 {
4379 let mut alive = alive_thread_ids.borrow_mut();
4380 for (id, entry) in global.threads.iter() {
4381 if thread_entry_marked_alive(marker, *id, entry) {
4382 alive.insert(*id);
4383 }
4384 }
4385 }
4386 record_live_interned_strings(&*global, marker, &live_interned_ids);
4387 };
4388 let budget = StepBudget::from_work(work_units);
4389 if let Some(target) = stop_target {
4390 global.heap.incremental_run_until_state_with_post_mark(
4391 &roots,
4392 target,
4393 work_units,
4394 hook,
4395 )
4396 } else {
4397 global.heap.incremental_step_with_post_mark(&roots, budget, hook)
4398 }
4399 };
4400
4401 if atomic_ran.get() {
4402 let alive_set = alive_ids.into_inner();
4403 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4404 let alive_thread_ids = alive_thread_ids.into_inner();
4405 let live_interned_ids = live_interned_ids.into_inner();
4406 let mut g = state_ref.global.borrow_mut();
4407 retain_live_interned_strings(&mut *g, live_interned_ids);
4408 g.weak_tables_registry.retain_identities(&alive_set);
4409 let main_thread_id = g.main_thread_id;
4410 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4411 g.cross_thread_upvals
4412 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4413 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4414 for object in &promoted {
4415 if let Some(ptr) = object.heap_ptr() {
4416 g.heap.move_finobj_to_tobefnz(ptr);
4417 }
4418 }
4419 }
4420
4421 let mut paused = matches!(outcome, StepOutcome::Paused);
4422 if target.is_none()
4423 && self._state.global().heap.gc_state() == lua_gc::GcState::CallFin
4424 && !self._state.global().finalizers.has_to_be_finalized()
4425 {
4426 paused = self._state.global().heap.finish_callfin_phase();
4427 }
4428
4429 paused
4430 }
4431
4432 pub fn prune_weak_tables_mark_only(&self) {
4439 use lua_gc::Trace;
4440 let state_ref: &LuaState = &*self._state;
4441
4442 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4443 let mut g = state_ref.global.borrow_mut();
4444 g.weak_tables_registry.live_snapshot_by_kind()
4445 };
4446 let interned_capacity = {
4447 let g = state_ref.global.borrow();
4448 g.interned_lt.len()
4449 };
4450
4451 let live_interned_ids: std::cell::RefCell<Vec<usize>> =
4452 std::cell::RefCell::new(Vec::new());
4453
4454 {
4455 let global = state_ref.global.borrow();
4456 global.heap.unpause();
4457 let roots = CollectRoots { global: &*global, thread: state_ref };
4458 let hook = |marker: &mut lua_gc::Marker| {
4459 live_interned_ids.borrow_mut().reserve(interned_capacity);
4460 trace_reachable_threads(&*global, global.current_thread_id, marker);
4461 loop {
4462 let visited_before = marker.visited_count();
4463 for t in &weak_tables_snapshot.ephemeron {
4464 let t_id = t.identity();
4465 if !marker.is_visited(t_id) {
4466 continue;
4467 }
4468 let to_mark = t.ephemeron_values_to_mark(
4469 &|id| marker.is_visited(id),
4470 );
4471 for v in &to_mark {
4472 v.trace(marker);
4473 }
4474 }
4475 marker.drain_gray_queue();
4476 if marker.visited_count() == visited_before {
4477 break;
4478 }
4479 }
4480 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4481 if marker.is_visited(t.identity()) {
4482 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4483 for v in &to_mark {
4484 v.trace(marker);
4485 }
4486 }
4487 }
4488 marker.drain_gray_queue();
4489 record_live_interned_strings(&*global, marker, &live_interned_ids);
4490 };
4491 global.heap.mark_only_with_post_mark(&roots, hook);
4492 }
4493
4494 let live_interned_ids = live_interned_ids.into_inner();
4495 let mut g = state_ref.global.borrow_mut();
4496 retain_live_interned_strings(&mut *g, live_interned_ids);
4497 }
4498
4499 pub fn change_mode(&self, mode: GcKind) {
4501 let old = self._state.global().gckind;
4502 if old == mode as u8 {
4503 self._state.global_mut().lastatomic = 0;
4504 return;
4505 }
4506 match mode {
4507 GcKind::Generational => {
4508 self.enter_generational_mode();
4509 }
4510 GcKind::Incremental => {
4511 self.enter_incremental_mode();
4512 }
4513 }
4514 }
4515
4516 pub fn fix_object<T: lua_gc::Trace + 'static>(&self, _o: &GcRef<T>) { }
4518
4519 pub fn free_all_objects(&self) {
4523 }
4525
4526 pub fn barrier(&self, p: &dyn std::any::Any, v: &LuaValue) {
4530 let g = self._state.global();
4531 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Forward);
4532 }
4533
4534 pub fn barrier_back(&self, p: &dyn std::any::Any, v: &LuaValue) {
4538 let g = self._state.global();
4539 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Backward);
4540 }
4541
4542 pub fn table_barrier_back(&self, p: &GcRef<LuaTable>, v: &LuaValue) {
4544 let g = self._state.global();
4545 barrier_lua_value(&g.heap, *p, v, g.is_gen_mode(), BarrierKind::Backward);
4546 }
4547
4548 pub fn obj_barrier(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4552 let g = self._state.global();
4553 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Forward);
4554 }
4555
4556 pub fn obj_barrier_back(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4559 let g = self._state.global();
4560 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Backward);
4561 }
4562}
4563
4564fn make_seed() -> u32 {
4573 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4574 {
4575 return crate::string::hash_bytes(b"lua-rs-wasm-seed", 0x9e37_79b9);
4576 }
4577
4578 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
4579 {
4580 use std::time::{SystemTime, UNIX_EPOCH};
4581 let t = SystemTime::now()
4582 .duration_since(UNIX_EPOCH)
4583 .map(|d| d.as_secs() as u32)
4584 .unwrap_or(0);
4585
4586 crate::string::hash_bytes(&t.to_le_bytes(), t)
4594 }
4595}
4596
4597pub(crate) fn set_debt(g: &mut GlobalState, mut debt: isize) {
4611 let tb = g.total_bytes() as isize;
4612 debug_assert!(tb > 0);
4613 if debt < tb.saturating_sub(isize::MAX) {
4615 debt = tb - isize::MAX;
4616 }
4617 g.gc_debt = debt;
4618}
4619
4620pub fn set_c_stack_limit(_state: &mut LuaState, _limit: u32) -> i32 {
4630 let _ = (_state, _limit);
4631 LUAI_MAXCCALLS as i32
4632}
4633
4634pub(crate) fn extend_ci(state: &mut LuaState) -> CallInfoIdx {
4651 debug_assert!(
4652 state.call_info[state.ci.0 as usize].next.is_none(),
4653 "extend_ci: current ci already has a cached next frame"
4654 );
4655
4656 let current_idx = state.ci;
4657 let new_idx = CallInfoIdx(state.call_info.len() as u32);
4659
4660 state.call_info.push(CallInfo {
4661 previous: Some(current_idx),
4662 next: None,
4663 u: CallInfoFrame::lua_default(),
4664 ..CallInfo::default()
4665 });
4666
4667 state.call_info[current_idx.0 as usize].next = Some(new_idx);
4668
4669 state.nci += 1;
4670
4671 new_idx
4672}
4673
4674fn free_ci(state: &mut LuaState) {
4695 let ci_idx = state.ci.0 as usize;
4696
4697 let mut next_opt = state.call_info[ci_idx].next.take();
4698
4699 while let Some(idx) = next_opt {
4700 next_opt = state.call_info[idx.0 as usize].next;
4701 state.nci = state.nci.saturating_sub(1);
4702 }
4703
4704 state.call_info.truncate(ci_idx + 1);
4707}
4708
4709pub(crate) fn shrink_ci(state: &mut LuaState) {
4736 let ci_idx = state.ci.0 as usize;
4737
4738 if state.call_info[ci_idx].next.is_none() {
4739 return;
4740 }
4741
4742 let free_count = state.call_info.len().saturating_sub(ci_idx + 1);
4743 if free_count <= 1 {
4744 return;
4745 }
4746
4747 let keep = free_count / 2;
4751 let removed = free_count - keep;
4752 let new_len = ci_idx + 1 + keep;
4753 state.call_info.truncate(new_len);
4754 state.nci = state.nci.saturating_sub(removed as u32);
4755
4756 if let Some(last) = state.call_info.last_mut() {
4758 last.next = None;
4759 }
4760}
4761
4762pub(crate) fn check_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4774 if state.c_calls() == LUAI_MAXCCALLS {
4777 return Err(LuaError::runtime(format_args!("C stack overflow")));
4778 }
4779 if state.c_calls() >= (LUAI_MAXCCALLS / 10 * 11) {
4781 return Err(LuaError::runtime(format_args!(
4784 "error while handling stack overflow (C stack overflow)"
4785 )));
4786 }
4787 Ok(())
4788}
4789
4790pub fn inc_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4801 state.n_ccalls += 1;
4802 if state.c_calls() >= LUAI_MAXCCALLS {
4804 check_c_stack(state)?;
4805 }
4806 Ok(())
4807}
4808
4809fn stack_init(thread: &mut LuaState) {
4815 let total_slots = BASIC_STACK_SIZE + EXTRA_STACK;
4817 thread.stack = vec![StackValue::default(); total_slots];
4818
4819 thread.tbclist = Vec::new();
4823
4824 thread.top = StackIdx(0);
4829
4830 thread.stack_last = StackIdx(BASIC_STACK_SIZE as u32);
4831
4832
4833 let base_ci = CallInfo {
4834 func: StackIdx(0),
4835 top: StackIdx(1 + LUA_MINSTACK as u32),
4836 previous: None,
4837 next: None,
4838 callstatus: CIST_C,
4839 nresults: 0,
4840 u: CallInfoFrame::c_default(),
4841 u2: CallInfoExtra::default(),
4842 };
4843
4844 if thread.call_info.is_empty() {
4845 thread.call_info.push(base_ci);
4846 } else {
4847 thread.call_info[0] = base_ci;
4848 thread.call_info.truncate(1);
4849 }
4850
4851 thread.stack[0] = StackValue { val: LuaValue::Nil, tbc_delta: 0 };
4852
4853 thread.top = StackIdx(1);
4854
4855 thread.ci = CallInfoIdx(0);
4856}
4857
4858fn free_stack(state: &mut LuaState) {
4859 if state.stack.is_empty() {
4860 return;
4861 }
4862 state.ci = CallInfoIdx(0);
4863 free_ci(state);
4864 debug_assert_eq!(state.nci, 0, "nci should be 0 after free_ci");
4865 state.stack.clear();
4867 state.stack.shrink_to_fit();
4868}
4869
4870fn init_registry(state: &mut LuaState) -> Result<(), LuaError> {
4871 let registry = state.new_table();
4873
4874 state.global_mut().l_registry = LuaValue::Table(registry.clone());
4876
4877 let globals = state.new_table();
4897 state.global_mut().globals = LuaValue::Table(globals);
4898 let loaded = state.new_table();
4899 state.global_mut().loaded = LuaValue::Table(loaded);
4900
4901 Ok(())
4902}
4903
4904fn lua_open(state: &mut LuaState) -> Result<(), LuaError> {
4905 stack_init(state);
4906 init_registry(state)?;
4907 crate::string::init(state)?;
4908 crate::tagmethods::init(state)?;
4909 state.global_mut().gcstp = 0;
4911 state.global().heap.unpause();
4912 state.global_mut().nilvalue = LuaValue::Nil;
4915 Ok(())
4917}
4918
4919fn preinit_thread(thread: &mut LuaState, global: Rc<RefCell<GlobalState>>) {
4920 thread.global = global;
4921 thread.stack = Vec::new();
4922 thread.call_info = Vec::new();
4923 thread.ci = CallInfoIdx(0);
4926 thread.nci = 0;
4927 thread.n_ccalls = 0;
4931 thread.hook = None;
4932 thread.hookmask = 0;
4933 thread.basehookcount = 0;
4934 thread.allowhook = true;
4935 thread.hookcount = thread.basehookcount;
4937
4938 {
4943 let (active, interval) = {
4944 let g = thread.global.borrow();
4945 (g.sandbox_active(), g.sandbox.interval.get())
4946 };
4947 if active {
4948 thread.hookmask = SANDBOX_COUNT_MASK;
4949 thread.basehookcount = interval;
4950 thread.hookcount = interval;
4951 }
4952 }
4953 thread.openupval = Vec::new();
4954 thread.status = LuaStatus::Ok as u8;
4955 thread.errfunc = 0;
4956 thread.oldpc = 0;
4957 thread.gc_check_needed = true;
4958}
4959
4960fn close_state(state: &mut LuaState) {
4961 let is_complete = state.global().is_complete();
4962
4963 if !is_complete {
4964 state.gc().free_all_objects();
4966 } else {
4967 state.ci = CallInfoIdx(0);
4968 state.gc().free_all_objects();
4971 }
4973
4974 state.global_mut().strt = StringPool::default();
4976
4977 free_stack(state);
4978
4979 }
4984
4985pub fn new_thread(state: &mut LuaState, initial_body: Option<LuaValue>) -> Result<(), LuaError> {
5014 state.gc().check_step();
5015
5016 let global_rc = state.global_rc();
5021 let hookmask = state.hookmask;
5022 let basehookcount = state.basehookcount;
5023
5024 let reserved_id = {
5025 let mut g = state.global_mut();
5026 let id = g.next_thread_id;
5027 g.next_thread_id += 1;
5028 id
5029 };
5030
5031 let mut new_thread = LuaState {
5032 status: LuaStatus::Ok as u8,
5033 allowhook: true,
5034 nci: 0,
5035 top: StackIdx(0),
5036 stack_last: StackIdx(0),
5037 stack: Vec::new(),
5038 ci: CallInfoIdx(0),
5039 call_info: Vec::new(),
5040 openupval: Vec::new(),
5041 tbclist: Vec::new(),
5042 global: global_rc.clone(),
5043 hook: None,
5044 hookmask: 0,
5045 basehookcount: 0,
5046 hookcount: 0,
5047 errfunc: 0,
5048 n_ccalls: 0,
5049 oldpc: 0,
5050 marked: 0,
5051 cached_thread_id: reserved_id,
5052 gc_check_needed: false,
5053 };
5054
5055 preinit_thread(&mut new_thread, global_rc);
5056
5057 new_thread.hookmask = hookmask;
5058 new_thread.basehookcount = basehookcount;
5059 new_thread.reset_hook_count();
5062
5063 stack_init(&mut new_thread);
5069
5070 if let Some(body) = initial_body {
5071 new_thread.push(body);
5072 }
5073
5074 let thread_ref: Rc<RefCell<LuaState>> = Rc::new(RefCell::new(new_thread));
5075
5076 let value = {
5077 let mut g = state.global_mut();
5078 let id = reserved_id;
5079 let value = GcRef::new(lua_types::value::LuaThread::new(id));
5080 g.threads.insert(
5081 id,
5082 ThreadRegistryEntry { state: thread_ref, value: value.clone() },
5083 );
5084 value
5085 };
5086
5087 state.push(LuaValue::Thread(value));
5088
5089 Ok(())
5090}
5091
5092pub fn reset_thread(state: &mut LuaState, status: i32) -> i32 {
5114 state.ci = CallInfoIdx(0);
5115 let ci_idx = 0usize;
5116
5117 if !state.stack.is_empty() {
5119 state.stack[0].val = LuaValue::Nil;
5120 }
5121
5122 state.call_info[ci_idx].func = StackIdx(0);
5123 state.call_info[ci_idx].callstatus = CIST_C;
5124
5125 let mut status = if status == LuaStatus::Yield as i32 {
5126 LuaStatus::Ok as i32
5127 } else {
5128 status
5129 };
5130
5131 state.status = LuaStatus::Ok as u8;
5132
5133 let close_status = crate::do_::close_protected(
5134 state,
5135 StackIdx(1),
5136 LuaStatus::from_raw(status),
5137 );
5138 status = close_status as i32;
5139
5140 if status != LuaStatus::Ok as i32 {
5141 crate::do_::set_error_obj(state, LuaStatus::from_raw(status), StackIdx(1));
5142 } else {
5143 state.top = StackIdx(1);
5144 }
5145
5146 let new_ci_top = StackIdx(state.top.0 + LUA_MINSTACK as u32);
5147 state.call_info[ci_idx].top = new_ci_top;
5148
5149 let needed = new_ci_top.0 as usize;
5152 if state.stack.len() < needed {
5153 state.stack.resize(needed, StackValue::default());
5154 }
5155
5156 status
5157}
5158
5159pub fn close_thread(state: &mut LuaState, from: Option<&LuaState>) -> i32 {
5173 state.n_ccalls = match from {
5175 Some(f) => f.c_calls(),
5176 None => 0,
5177 };
5178 let current_status = state.status as i32;
5179 let result = reset_thread(state, current_status);
5180 result
5181}
5182
5183pub fn reset_thread_api(state: &mut LuaState) -> i32 {
5192 close_thread(state, None)
5193}
5194
5195pub fn new_state() -> Option<LuaState> {
5231 let placeholder_str = GcRef::new(LuaString::placeholder());
5240
5241 let initial_white = 1u8 << WHITE0BIT;
5243
5244 let global = GlobalState {
5248 parser_hook: None,
5249 cli_argv: None,
5250 cli_preload: None,
5251 lua_version: lua_types::LuaVersion::default(),
5252 file_loader_hook: None,
5253 file_open_hook: None,
5254 stdout_hook: None,
5255 stderr_hook: None,
5256 stdin_hook: None,
5257 env_hook: None,
5258 unix_time_hook: None,
5259 cpu_clock_hook: None,
5260 local_offset_hook: None,
5261 entropy_hook: None,
5262 temp_name_hook: None,
5263 popen_hook: None,
5264 file_remove_hook: None,
5265 file_rename_hook: None,
5266 os_execute_hook: None,
5267 dynlib_load_hook: None,
5268 dynlib_symbol_hook: None,
5269 dynlib_unload_hook: None,
5270 sandbox: SandboxLimits::default(),
5271 gc_debt: 0,
5272 gc_estimate: 0,
5273 lastatomic: 0,
5274 strt: StringPool::default(),
5275 l_registry: LuaValue::Nil,
5276 external_roots: ExternalRootSet::default(),
5277 globals: LuaValue::Nil,
5278 loaded: LuaValue::Nil,
5279 nilvalue: LuaValue::Int(0),
5280 seed: make_seed(),
5281 currentwhite: initial_white,
5282 gcstate: GCS_PAUSE,
5283 gckind: GcKind::Incremental as u8,
5285 gcstopem: false,
5286 genminormul: LUAI_GENMINORMUL,
5287 genmajormul: (LUAI_GENMAJORMUL / 4) as u8,
5289 gcstp: GCSTPGC,
5290 gcemergency: false,
5291 gcpause: (LUAI_GCPAUSE / 4) as u8,
5292 gcstepmul: (LUAI_GCMUL / 4) as u8,
5293 gcstepsize: LUAI_GCSTEPSIZE,
5294 gc55_params: [20, 50, 68, 250, 200, 9600],
5297 sweepgc_cursor: 0,
5298 weak_tables_registry: lua_gc::WeakRegistry::default(),
5299 finalizers: lua_gc::FinalizerRegistry::default(),
5300 gc_finalizer_error: None,
5301 twups: Vec::new(),
5302 panic: None,
5303 mainthread: None,
5304 threads: std::collections::HashMap::new(),
5305 main_thread_value: GcRef::new(lua_types::value::LuaThread::new(0)),
5306 current_thread_id: 0,
5307 main_thread_id: 0,
5308 next_thread_id: 1,
5309 memerrmsg: placeholder_str.clone(),
5310 tmname: Vec::new(),
5311 mt: std::array::from_fn(|_| None),
5312 strcache: std::array::from_fn(|_| {
5313 std::array::from_fn(|_| placeholder_str.clone())
5314 }),
5315 interned_lt: InternedStringMap::default(),
5316 warnf: None,
5317 warn_mode: WarnMode::Off,
5318 test_warn_enabled: false,
5319 test_warn_on: false,
5320 test_warn_mode: TestWarnMode::Normal,
5321 test_warn_last_to_cont: false,
5322 test_warn_buffer: Vec::new(),
5323 c_functions: Vec::new(),
5324 heap: lua_gc::Heap::new(),
5325 cross_thread_upvals: std::collections::HashMap::new(),
5326 suspended_parent_stacks: Vec::new(),
5327 suspended_parent_open_upvals: Vec::new(),
5328 };
5329
5330 let global_rc = Rc::new(RefCell::new(global));
5331
5332 let initial_marked = initial_white;
5334
5335 let mut main_thread = LuaState {
5336 status: LuaStatus::Ok as u8,
5337 allowhook: true,
5338 nci: 0,
5339 top: StackIdx(0),
5340 stack_last: StackIdx(0),
5341 stack: Vec::new(),
5342 ci: CallInfoIdx(0),
5343 call_info: Vec::new(),
5344 openupval: Vec::new(),
5345 tbclist: Vec::new(),
5346 global: global_rc.clone(),
5347 hook: None,
5348 hookmask: 0,
5349 basehookcount: 0,
5350 hookcount: 0,
5351 errfunc: 0,
5352 n_ccalls: 0,
5353 oldpc: 0,
5354 marked: initial_marked,
5355 cached_thread_id: 0,
5356 gc_check_needed: false,
5357 };
5358
5359 preinit_thread(&mut main_thread, global_rc.clone());
5360
5361 main_thread.inc_nny();
5363
5364 match lua_open(&mut main_thread) {
5374 Ok(()) => {}
5375 Err(_) => {
5376 close_state(&mut main_thread);
5377 return None;
5378 }
5379 }
5380
5381 Some(main_thread)
5382}
5383
5384pub fn close(mut state: LuaState) {
5400 close_state(&mut state);
5405}
5406
5407pub(crate) fn warning(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5417 let test_warn_enabled = state.global().test_warn_enabled;
5418 if test_warn_enabled {
5419 test_warn(state, msg, to_cont);
5420 return;
5421 }
5422
5423 let has_warnf = state.global().warnf.is_some();
5432 if has_warnf {
5433 let mut warnf = state.global_mut().warnf.take();
5435 if let Some(ref mut f) = warnf {
5436 f(msg, to_cont);
5437 }
5438 state.global_mut().warnf = warnf;
5440 return;
5441 }
5442 default_warn(state, msg, to_cont);
5443}
5444
5445fn test_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5446 let is_control = {
5447 let g = state.global();
5448 !g.test_warn_last_to_cont && !to_cont && msg.first() == Some(&b'@')
5449 };
5450 if is_control {
5451 let mut g = state.global_mut();
5452 match &msg[1..] {
5453 b"off" => g.test_warn_on = false,
5454 b"on" => g.test_warn_on = true,
5455 b"normal" => g.test_warn_mode = TestWarnMode::Normal,
5456 b"allow" => g.test_warn_mode = TestWarnMode::Allow,
5457 b"store" => g.test_warn_mode = TestWarnMode::Store,
5458 _ => {}
5459 }
5460 return;
5461 }
5462
5463 let finished = {
5464 let mut g = state.global_mut();
5465 g.test_warn_last_to_cont = to_cont;
5466 g.test_warn_buffer.extend_from_slice(msg);
5467 if to_cont {
5468 None
5469 } else {
5470 Some((
5471 std::mem::take(&mut g.test_warn_buffer),
5472 g.test_warn_mode,
5473 g.test_warn_on,
5474 ))
5475 }
5476 };
5477
5478 let Some((message, mode, warn_on)) = finished else {
5479 return;
5480 };
5481 match mode {
5482 TestWarnMode::Normal => {
5483 if warn_on && message.first() == Some(&b'#') {
5484 write_warning_message(&message);
5485 }
5486 }
5487 TestWarnMode::Allow => {
5488 if warn_on {
5489 write_warning_message(&message);
5490 }
5491 }
5492 TestWarnMode::Store => {
5493 if let Ok(s) = state.intern_str(&message) {
5494 state.push(LuaValue::Str(s));
5495 let _ = crate::api::set_global(state, b"_WARN");
5496 }
5497 }
5498 }
5499}
5500
5501fn write_warning_message(message: &[u8]) {
5502 use std::io::Write;
5503 let stderr = std::io::stderr();
5504 let mut h = stderr.lock();
5505 let _ = h.write_all(b"Lua warning: ");
5506 let _ = h.write_all(message);
5507 let _ = h.write_all(b"\n");
5508}
5509
5510fn default_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5515 use std::io::Write;
5516 if !to_cont && msg.first() == Some(&b'@') {
5518 match &msg[1..] {
5519 b"off" => state.global_mut().warn_mode = WarnMode::Off,
5520 b"on" => state.global_mut().warn_mode = WarnMode::On,
5521 _ => {}
5522 }
5523 return;
5524 }
5525 let mode = state.global().warn_mode;
5526 match mode {
5527 WarnMode::Off => {}
5528 WarnMode::On | WarnMode::Cont => {
5529 let stderr = std::io::stderr();
5530 let mut h = stderr.lock();
5531 if mode == WarnMode::On {
5532 let _ = h.write_all(b"Lua warning: ");
5533 }
5534 let _ = h.write_all(msg);
5535 if to_cont {
5536 state.global_mut().warn_mode = WarnMode::Cont;
5537 } else {
5538 let _ = h.write_all(b"\n");
5539 state.global_mut().warn_mode = WarnMode::On;
5540 }
5541 }
5542 }
5543}
5544
5545#[cfg(test)]
5546mod tests {
5547 use super::*;
5548
5549 fn test_noop_cclosure(_: &mut LuaState) -> Result<usize, LuaError> {
5550 Ok(0)
5551 }
5552
5553 #[test]
5554 fn external_root_keys_reject_stale_slot_after_reuse() {
5555 let mut roots = ExternalRootSet::default();
5556
5557 let first = roots.insert(LuaValue::Int(1));
5558 assert_eq!(roots.len(), 1);
5559 assert_eq!(roots.get(first), Some(&LuaValue::Int(1)));
5560
5561 assert_eq!(roots.remove(first), Some(LuaValue::Int(1)));
5562 assert!(roots.get(first).is_none());
5563 assert!(roots.remove(first).is_none());
5564 assert_eq!(roots.len(), 0);
5565 assert_eq!(roots.vacant_len(), 1);
5566 assert!(roots.replace(first, LuaValue::Int(9)).is_none());
5567 assert!(roots.is_empty());
5568
5569 let second = roots.insert(LuaValue::Int(2));
5570 assert_eq!(first.index, second.index);
5571 assert_ne!(first, second);
5572 assert!(roots.get(first).is_none());
5573 assert_eq!(roots.get(second), Some(&LuaValue::Int(2)));
5574 assert!(roots.replace(first, LuaValue::Int(3)).is_none());
5575 }
5576
5577 #[test]
5578 fn external_roots_keep_heap_value_alive_until_unrooted() {
5579 let mut state = new_state().expect("state should initialize");
5580 let _heap_guard = {
5581 let g = state.global();
5582 lua_gc::HeapGuard::push(&g.heap)
5583 };
5584
5585 let table = state.new_table();
5586 assert_eq!(state.global().heap.allgc_count(), 1);
5587
5588 let key = state.external_root_value(LuaValue::Table(table));
5589 state.gc().full_collect();
5590 assert_eq!(state.global().heap.allgc_count(), 1);
5591 assert_eq!(state.global().external_roots.len(), 1);
5592
5593 assert!(state.external_unroot_value(key).is_some());
5594 state.gc().full_collect();
5595 assert_eq!(state.global().heap.allgc_count(), 0);
5596 assert!(state.global().external_roots.is_empty());
5597 }
5598
5599 #[test]
5600 fn table_buffer_accounting_refunds_on_sweep() {
5601 let mut state = new_state().expect("state should initialize");
5602 let _heap_guard = {
5603 let g = state.global();
5604 lua_gc::HeapGuard::push(&g.heap)
5605 };
5606
5607 let table = state.new_table();
5608 let key = state.external_root_value(LuaValue::Table(table));
5609 let header_bytes = state.global().heap.bytes_used();
5610 assert!(header_bytes > 0);
5611
5612 for i in 1..=128 {
5613 table
5614 .raw_set_int(&mut state, i, LuaValue::Int(i))
5615 .expect("integer table insert should succeed");
5616 }
5617 let grown_bytes = state.global().heap.bytes_used();
5618 assert!(
5619 grown_bytes > header_bytes,
5620 "table array/hash buffer growth must be charged to the GC heap"
5621 );
5622
5623 state.gc().full_collect();
5624 assert_eq!(
5625 state.global().heap.bytes_used(),
5626 grown_bytes,
5627 "rooted table buffer bytes should remain charged after collection"
5628 );
5629
5630 assert!(state.external_unroot_value(key).is_some());
5631 state.gc().full_collect();
5632 assert_eq!(state.global().heap.bytes_used(), 0);
5633 assert_eq!(state.global().heap.allgc_count(), 0);
5634 }
5635
5636 #[test]
5637 fn userdata_buffer_accounting_refunds_on_sweep() {
5638 let mut state = new_state().expect("state should initialize");
5639 let _heap_guard = {
5640 let g = state.global();
5641 lua_gc::HeapGuard::push(&g.heap)
5642 };
5643
5644 let payload_len = 4096;
5645 let userdata = state
5646 .new_userdata_typed(b"accounting", payload_len, 3)
5647 .expect("userdata allocation should succeed");
5648 state.pop_n(1);
5649 let key = state.external_root_value(LuaValue::UserData(userdata));
5650 let allocated_bytes = state.global().heap.bytes_used();
5651 assert!(
5652 allocated_bytes > payload_len,
5653 "userdata payload 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 userdata payload 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 cclosure_upvalue_accounting_refunds_on_sweep() {
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 nupvalues = 64;
5678 for i in 0..nupvalues {
5679 state.push(LuaValue::Int(i as i64));
5680 }
5681 crate::api::push_cclosure(&mut state, test_noop_cclosure, nupvalues as i32)
5682 .expect("C closure creation should succeed");
5683 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5684 panic!("expected heavy C closure");
5685 };
5686 let expected_payload = ccl.buffer_bytes();
5687 let key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
5688 state.pop_n(1);
5689 let allocated_bytes = state.global().heap.bytes_used();
5690 assert!(
5691 allocated_bytes >= expected_payload,
5692 "C closure upvalue vector bytes must be charged to the GC heap"
5693 );
5694
5695 state.gc().full_collect();
5696 assert_eq!(
5697 state.global().heap.bytes_used(),
5698 allocated_bytes,
5699 "rooted C closure payload bytes should remain charged after collection"
5700 );
5701
5702 assert!(state.external_unroot_value(key).is_some());
5703 state.gc().full_collect();
5704 assert_eq!(state.global().heap.bytes_used(), 0);
5705 assert_eq!(state.global().heap.allgc_count(), 0);
5706 }
5707
5708 #[test]
5709 fn proto_and_lclosure_accounting_refunds_on_sweep() {
5710 let mut state = new_state().expect("state should initialize");
5711 let _heap_guard = {
5712 let g = state.global();
5713 lua_gc::HeapGuard::push(&g.heap)
5714 };
5715
5716 let mut proto = LuaProto::placeholder();
5717 proto.code = vec![lua_types::opcode::Instruction(0); 2048];
5718 proto.lineinfo = vec![0; 2048];
5719 proto.k = vec![LuaValue::Int(1); 512];
5720 let expected_proto_payload = proto.buffer_bytes();
5721 let proto = GcRef::new(proto);
5722 proto.account_buffer(expected_proto_payload as isize);
5723
5724 let closure = state.new_lclosure(proto, 16);
5725 let expected_closure_payload = closure.buffer_bytes();
5726 let key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5727 let allocated_bytes = state.global().heap.bytes_used();
5728 assert!(
5729 allocated_bytes >= expected_proto_payload + expected_closure_payload,
5730 "proto and Lua closure vector bytes must be charged to the GC heap"
5731 );
5732
5733 state.gc().full_collect();
5734 assert_eq!(
5735 state.global().heap.bytes_used(),
5736 allocated_bytes,
5737 "rooted proto and Lua closure payload bytes should remain charged after collection"
5738 );
5739
5740 assert!(state.external_unroot_value(key).is_some());
5741 state.gc().full_collect();
5742 assert_eq!(state.global().heap.bytes_used(), 0);
5743 assert_eq!(state.global().heap.allgc_count(), 0);
5744 }
5745
5746 #[test]
5747 fn string_buffer_accounting_refunds_on_sweep() {
5748 let mut state = new_state().expect("state should initialize");
5749 let _heap_guard = {
5750 let g = state.global();
5751 lua_gc::HeapGuard::push(&g.heap)
5752 };
5753
5754 let payload = vec![b'x'; crate::string::MAX_SHORT_LEN + 4096];
5755 let string = state.intern_str(&payload).expect("long string should allocate");
5756 let key = state.external_root_value(LuaValue::Str(string));
5757 let allocated_bytes = state.global().heap.bytes_used();
5758 assert!(
5759 allocated_bytes > payload.len(),
5760 "long string backing bytes must be charged to the GC heap"
5761 );
5762
5763 state.gc().full_collect();
5764 assert_eq!(
5765 state.global().heap.bytes_used(),
5766 allocated_bytes,
5767 "rooted string buffer bytes should remain charged after collection"
5768 );
5769
5770 assert!(state.external_unroot_value(key).is_some());
5771 state.gc().full_collect();
5772 assert_eq!(state.global().heap.bytes_used(), 0);
5773 assert_eq!(state.global().heap.allgc_count(), 0);
5774 }
5775
5776 #[test]
5777 fn interned_short_string_cache_does_not_root_unreferenced_string() {
5778 let mut state = new_state().expect("state should initialize");
5779 let _heap_guard = {
5780 let g = state.global();
5781 lua_gc::HeapGuard::push(&g.heap)
5782 };
5783
5784 let payload = b"weak-cache-probe-a";
5785 let string = state
5786 .intern_str(payload)
5787 .expect("short string should intern");
5788 let id = string.identity();
5789 assert!(state.global().interned_lt.contains_key(&payload[..]));
5790 assert!(state.global().heap.allocation_token(id).is_some());
5791
5792 state.gc().full_collect();
5793 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5794 assert_eq!(state.global().heap.allocation_token(id), None);
5795 }
5796
5797 #[test]
5798 fn interned_short_string_cache_keeps_reachable_string_until_unrooted() {
5799 let mut state = new_state().expect("state should initialize");
5800 let _heap_guard = {
5801 let g = state.global();
5802 lua_gc::HeapGuard::push(&g.heap)
5803 };
5804
5805 let payload = b"weak-cache-probe-b";
5806 let string = state
5807 .intern_str(payload)
5808 .expect("short string should intern");
5809 let id = string.identity();
5810 let key = state.external_root_value(LuaValue::Str(string));
5811
5812 state.gc().full_collect();
5813 assert!(state.global().interned_lt.contains_key(&payload[..]));
5814 assert!(state.global().heap.allocation_token(id).is_some());
5815
5816 assert!(state.external_unroot_value(key).is_some());
5817 state.gc().full_collect();
5818 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5819 assert_eq!(state.global().heap.allocation_token(id), None);
5820 }
5821
5822 #[test]
5823 fn gc_phase_predicates_follow_heap_state() {
5824 let mut state = new_state().expect("state should initialize");
5825 let _heap_guard = {
5826 let g = state.global();
5827 lua_gc::HeapGuard::push(&g.heap)
5828 };
5829
5830 {
5831 let mut g = state.global_mut();
5832 g.gckind = GcKind::Incremental as u8;
5833 g.lastatomic = 0;
5834 assert!(!g.is_gen_mode());
5835 g.lastatomic = 1;
5836 assert!(g.is_gen_mode());
5837 g.lastatomic = 0;
5838 }
5839
5840 let mut roots = Vec::new();
5841 for _ in 0..16 {
5842 let table = state.new_table();
5843 roots.push(state.external_root_value(LuaValue::Table(table)));
5844 }
5845
5846 let mut saw_keep = false;
5847 let mut saw_sweep = false;
5848 for _ in 0..128 {
5849 state.gc().incremental_step(1);
5850 let g = state.global();
5851 let heap_state = g.heap.gc_state();
5852 assert_eq!(
5853 g.keep_invariant(),
5854 heap_state.is_invariant()
5855 );
5856 assert_eq!(g.is_sweep_phase(), heap_state.is_sweep());
5857 saw_keep |= g.keep_invariant();
5858 saw_sweep |= g.is_sweep_phase();
5859 if heap_state.is_pause() && saw_keep && saw_sweep {
5860 break;
5861 }
5862 }
5863
5864 assert!(saw_keep, "incremental cycle should expose an invariant phase");
5865 assert!(saw_sweep, "incremental cycle should expose a sweep phase");
5866
5867 for key in roots {
5868 assert!(state.external_unroot_value(key).is_some());
5869 }
5870 state.gc().full_collect();
5871 }
5872
5873 #[test]
5874 fn gc_barrier_keeps_new_child_stored_in_black_parent() {
5875 let mut state = new_state().expect("state should initialize");
5876 let _heap_guard = {
5877 let g = state.global();
5878 lua_gc::HeapGuard::push(&g.heap)
5879 };
5880
5881 let parent = state.new_table();
5882 let parent_key = state.external_root_value(LuaValue::Table(parent));
5883 state.gc().incremental_step(1);
5884 assert!(
5885 state.global().keep_invariant(),
5886 "test setup should leave the parent marked during an active cycle"
5887 );
5888
5889 let child = state.new_table();
5890 let parent_value = LuaValue::Table(parent);
5891 let child_value = LuaValue::Table(child);
5892 parent
5893 .raw_set_int(&mut state, 1, child_value)
5894 .expect("table store should succeed");
5895 state.gc_barrier_back(&parent_value, &child_value);
5896
5897 for _ in 0..128 {
5898 if state.gc().incremental_step(1) {
5899 break;
5900 }
5901 }
5902
5903 assert_eq!(state.global().heap.allgc_count(), 2);
5904 assert_eq!(
5905 parent.get_int(1).as_table().map(|t| t.identity()),
5906 Some(child.identity())
5907 );
5908
5909 assert!(state.external_unroot_value(parent_key).is_some());
5910 state.gc().full_collect();
5911 assert_eq!(state.global().heap.allgc_count(), 0);
5912 }
5913
5914 #[test]
5915 fn generational_mode_promotes_and_barriers_age_objects() {
5916 let mut state = new_state().expect("state should initialize");
5917 let _heap_guard = {
5918 let g = state.global();
5919 lua_gc::HeapGuard::push(&g.heap)
5920 };
5921
5922 let parent = state.new_table();
5923 let parent_key = state.external_root_value(LuaValue::Table(parent));
5924
5925 state.gc().change_mode(GcKind::Generational);
5926 assert_eq!(parent.0.age(), lua_gc::GcAge::Old);
5927 assert_eq!(parent.0.color(), lua_gc::Color::Black);
5928 let majorbase = state.global().gc_estimate;
5929 assert!(majorbase > 0);
5930 assert!(state.global().gc_debt() <= 0);
5931
5932 let child = state.new_table();
5933 let parent_value = LuaValue::Table(parent);
5934 let child_value = LuaValue::Table(child);
5935 parent
5936 .raw_set_int(&mut state, 1, child_value.clone())
5937 .expect("table store should succeed");
5938 state.gc_barrier_back(&parent_value, &child_value);
5939 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched1);
5940 assert_eq!(parent.0.color(), lua_gc::Color::Gray);
5941 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5942
5943 let metatable = state.new_table();
5944 parent.set_metatable(Some(metatable));
5945 state.gc().obj_barrier(&parent, &metatable);
5946 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old0);
5947
5948 assert!(state.gc().generational_step_minor_only());
5949 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched2);
5950 assert_eq!(child.0.age(), lua_gc::GcAge::Survival);
5951 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old1);
5952 assert_eq!(state.global().gc_estimate, majorbase);
5953 assert!(state.global().gc_debt() <= 0);
5954
5955 state.gc().change_mode(GcKind::Incremental);
5956 assert_eq!(parent.0.age(), lua_gc::GcAge::New);
5957 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5958 assert_eq!(metatable.0.age(), lua_gc::GcAge::New);
5959
5960 assert!(state.external_unroot_value(parent_key).is_some());
5961 state.gc().full_collect();
5962 }
5963
5964 #[test]
5965 fn generational_upvalue_write_barrier_marks_young_child_old0() {
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 proto = state.new_proto();
5973 let closure = state.new_lclosure(proto, 1);
5974 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5975 state.gc().change_mode(GcKind::Generational);
5976 let uv = closure.upval(0);
5977 assert_eq!(uv.0.age(), lua_gc::GcAge::Old);
5978
5979 let child = state.new_table();
5980 state
5981 .upvalue_set(&closure, 0, LuaValue::Table(child))
5982 .expect("closed upvalue write should succeed");
5983 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
5984
5985 assert!(state.external_unroot_value(closure_key).is_some());
5986 state.gc().full_collect();
5987 }
5988
5989 #[test]
5990 fn cclosure_setupvalue_replaces_upvalue() {
5991 let mut state = new_state().expect("state should initialize");
5992 let _heap_guard = {
5993 let g = state.global();
5994 lua_gc::HeapGuard::push(&g.heap)
5995 };
5996
5997 let first = state.new_table();
5998 state.push(LuaValue::Table(first));
5999 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6000 .expect("C closure creation should succeed");
6001 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6002 panic!("expected heavy C closure");
6003 };
6004
6005 let second = state.new_table();
6006 state.push(LuaValue::Table(second));
6007 let name = crate::api::setup_value(&mut state, -2, 1)
6008 .expect("C closure upvalue should exist");
6009
6010 assert!(name.is_empty());
6011 let upvalues = ccl.upvalues.borrow();
6012 let LuaValue::Table(actual) = upvalues[0].clone() else {
6013 panic!("expected table upvalue");
6014 };
6015 assert_eq!(actual.identity(), second.identity());
6016 }
6017
6018 #[test]
6019 fn generational_cclosure_setupvalue_barrier_marks_young_child_old0() {
6020 let mut state = new_state().expect("state should initialize");
6021 let _heap_guard = {
6022 let g = state.global();
6023 lua_gc::HeapGuard::push(&g.heap)
6024 };
6025
6026 state.push(LuaValue::Nil);
6027 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6028 .expect("C closure creation should succeed");
6029 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6030 panic!("expected heavy C closure");
6031 };
6032 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
6033
6034 state.gc().change_mode(GcKind::Generational);
6035 assert_eq!(ccl.0.age(), lua_gc::GcAge::Old);
6036
6037 let child = state.new_table();
6038 state.push(LuaValue::Table(child));
6039 crate::api::setup_value(&mut state, -2, 1)
6040 .expect("C closure upvalue should exist");
6041
6042 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
6043
6044 assert!(state.external_unroot_value(closure_key).is_some());
6045 state.gc().full_collect();
6046 }
6047
6048 #[test]
6049 fn generational_closure_upvalue_slot_barrier_marks_new_upval_old0() {
6050 let mut state = new_state().expect("state should initialize");
6051 let _heap_guard = {
6052 let g = state.global();
6053 lua_gc::HeapGuard::push(&g.heap)
6054 };
6055
6056 let proto = state.new_proto();
6057 let closure = state.new_lclosure(proto, 1);
6058 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6059 state.gc().change_mode(GcKind::Generational);
6060 assert_eq!(closure.0.age(), lua_gc::GcAge::Old);
6061
6062 let replacement = state.new_upval_closed(LuaValue::Nil);
6063 closure.set_upval(0, replacement);
6064 state.gc().obj_barrier(&closure, &replacement);
6065 assert_eq!(replacement.0.age(), lua_gc::GcAge::Old0);
6066
6067 assert!(state.external_unroot_value(closure_key).is_some());
6068 state.gc().full_collect();
6069 }
6070
6071 #[test]
6072 fn cross_thread_upvalue_mirror_traces_values_as_roots() {
6073 let mut state = new_state().expect("state should initialize");
6074 let _heap_guard = {
6075 let g = state.global();
6076 lua_gc::HeapGuard::push(&g.heap)
6077 };
6078
6079 let mirrored = state.new_table();
6080 state
6081 .global_mut()
6082 .cross_thread_upvals
6083 .insert((999, StackIdx(0)), LuaValue::Table(mirrored));
6084
6085 state.gc().full_collect();
6086 assert_eq!(state.global().heap.allgc_count(), 1);
6087
6088 state.global_mut().cross_thread_upvals.clear();
6089 state.gc().full_collect();
6090 assert_eq!(state.global().heap.allgc_count(), 0);
6091 }
6092
6093 #[test]
6094 fn generational_full_collect_promotes_new_survivors_to_old() {
6095 let mut state = new_state().expect("state should initialize");
6096 let _heap_guard = {
6097 let g = state.global();
6098 lua_gc::HeapGuard::push(&g.heap)
6099 };
6100
6101 state.gc().change_mode(GcKind::Generational);
6102 let table = state.new_table();
6103 let table_key = state.external_root_value(LuaValue::Table(table));
6104 assert_eq!(table.0.age(), lua_gc::GcAge::New);
6105
6106 state.gc().full_collect();
6107 assert_eq!(table.0.age(), lua_gc::GcAge::Old);
6108 assert_eq!(table.0.color(), lua_gc::Color::Black);
6109
6110 assert!(state.external_unroot_value(table_key).is_some());
6111 state.gc().full_collect();
6112 }
6113
6114 #[test]
6115 fn gc_packed_params_return_user_visible_values() {
6116 let mut state = new_state().expect("state should initialize");
6117 assert_eq!(
6118 crate::api::gc(&mut state, crate::api::GcArgs::SetPause { value: 200 }),
6119 200
6120 );
6121 assert_eq!(state.global().gc_pause_param(), 200);
6122 assert_eq!(
6123 crate::api::gc(&mut state, crate::api::GcArgs::SetStepMul { value: 200 }),
6124 100
6125 );
6126 assert_eq!(state.global().gc_stepmul_param(), 200);
6127
6128 crate::api::gc(
6129 &mut state,
6130 crate::api::GcArgs::Gen {
6131 minormul: 0,
6132 majormul: 200,
6133 },
6134 );
6135 assert_eq!(state.global().gc_genmajormul_param(), 200);
6136 }
6137
6138 #[test]
6139 fn generational_step_runs_bad_major_when_growth_exceeds_genmajormul() {
6140 let mut state = new_state().expect("state should initialize");
6141 let _heap_guard = {
6142 let g = state.global();
6143 lua_gc::HeapGuard::push(&g.heap)
6144 };
6145
6146 let root = state.new_table();
6147 let root_key = state.external_root_value(LuaValue::Table(root));
6148 state.gc().change_mode(GcKind::Generational);
6149
6150 let root_value = LuaValue::Table(root);
6151 for i in 1..=64 {
6152 let child = state.new_table();
6153 let child_value = LuaValue::Table(child);
6154 root
6155 .raw_set_int(&mut state, i, child_value.clone())
6156 .expect("table store should succeed");
6157 state.gc_barrier_back(&root_value, &child_value);
6158 }
6159
6160 {
6161 let mut g = state.global_mut();
6162 g.gc_estimate = 1;
6163 set_debt(&mut *g, 1);
6164 }
6165
6166 assert!(state.gc().generational_step());
6167 let g = state.global();
6168 assert!(g.is_gen_mode());
6169 assert!(g.lastatomic > 0, "bad major collection should arm stepgenfull");
6170 assert!(g.gc_estimate > 1);
6171 assert!(g.gc_debt() <= 0);
6172 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6173 drop(g);
6174
6175 assert!(state.external_unroot_value(root_key).is_some());
6176 state.gc().full_collect();
6177 }
6178
6179 #[test]
6180 fn generational_implicit_step_runs_major_when_heap_threshold_exceeded() {
6181 let mut state = new_state().expect("state should initialize");
6182 let _heap_guard = {
6183 let g = state.global();
6184 lua_gc::HeapGuard::push(&g.heap)
6185 };
6186
6187 let root = state.new_table();
6188 let root_key = state.external_root_value(LuaValue::Table(root));
6189 state.gc().change_mode(GcKind::Generational);
6190
6191 let root_value = LuaValue::Table(root);
6192 for i in 1..=64 {
6193 let child = state.new_table();
6194 let child_value = LuaValue::Table(child);
6195 root
6196 .raw_set_int(&mut state, i, child_value.clone())
6197 .expect("table store should succeed");
6198 state.gc_barrier_back(&root_value, &child_value);
6199 }
6200
6201 {
6202 let mut g = state.global_mut();
6203 g.gc_estimate = 1;
6204 set_debt(&mut *g, -1);
6205 g.heap.set_threshold_bytes(1);
6206 }
6207
6208 assert!(state.gc().generational_step());
6209 let g = state.global();
6210 assert!(g.is_gen_mode());
6211 assert!(
6212 g.lastatomic > 0,
6213 "implicit threshold-triggered growth should arm a bad major"
6214 );
6215 assert!(g.gc_debt() <= 0);
6216 drop(g);
6217
6218 assert!(state.external_unroot_value(root_key).is_some());
6219 state.gc().full_collect();
6220 }
6221
6222 #[test]
6223 fn generational_stepgenfull_returns_to_gen_after_good_collection() {
6224 let mut state = new_state().expect("state should initialize");
6225 let _heap_guard = {
6226 let g = state.global();
6227 lua_gc::HeapGuard::push(&g.heap)
6228 };
6229
6230 let root = state.new_table();
6231 let root_key = state.external_root_value(LuaValue::Table(root));
6232 state.gc().change_mode(GcKind::Generational);
6233 {
6234 let mut g = state.global_mut();
6235 g.lastatomic = 1024;
6236 }
6237
6238 assert!(state.gc().generational_step());
6239 let g = state.global();
6240 assert_eq!(g.gckind, GcKind::Generational as u8);
6241 assert_eq!(g.lastatomic, 0);
6242 assert!(g.gc_debt() <= 0);
6243 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6244 assert_eq!(root.0.color(), lua_gc::Color::Black);
6245 drop(g);
6246
6247 assert!(state.external_unroot_value(root_key).is_some());
6248 state.gc().full_collect();
6249 }
6250
6251 #[test]
6252 fn generational_step_zero_reports_false_without_positive_debt() {
6253 let mut state = new_state().expect("state should initialize");
6254 let _heap_guard = {
6255 let g = state.global();
6256 lua_gc::HeapGuard::push(&g.heap)
6257 };
6258
6259 state.gc().change_mode(GcKind::Generational);
6260 assert_eq!(
6261 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 0 }),
6262 0
6263 );
6264 assert_eq!(
6265 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 1 }),
6266 1
6267 );
6268 }
6269}
6270
6271