1#![allow(dead_code)]
9
10#[allow(unused_imports)]
11use crate::prelude::*;
12use std::convert::Infallible;
13
14use crate::state::{
15 FinalizerObject, LuaCFunction, LuaCallable, LuaState, LuaTableRefExt, LuaTypeExt,
16 LuaUserDataRefExt, LuaValueExt, StackIdx, StackIdxExt, WeakTableEntry,
17};
18use lua_types::value::LuaTable;
19use lua_types::{
20 GcRef, LuaClosure, LuaError, LuaStatus, LuaString, LuaType, LuaUserData, LuaValue,
21};
22
23pub const LUA_IDENT: &[u8] = b"$LuaVersion: Lua 5.4.7 Copyright (C) 1994-2024 Lua.org, PUC-Rio $\
24 $LuaAuthors: R. Ierusalimschy, L. H. Figueiredo, W. Celes $";
25
26const LUA_REGISTRYINDEX: i32 = -(1_000_000) - 1000;
27
28const LUA_MULTRET: i32 = -1;
29
30const LUA_RIDX_GLOBALS: i64 = 2;
31
32const MAX_UPVAL: u8 = 255;
33
34#[inline]
35fn is_pseudo(idx: i32) -> bool {
36 idx <= LUA_REGISTRYINDEX
37}
38
39#[inline]
40fn is_upvalue(idx: i32) -> bool {
41 idx < LUA_REGISTRYINDEX
42}
43
44#[inline]
49fn is_valid_index(state: &LuaState, idx: i32) -> bool {
50 if idx == 0 {
51 return false;
52 }
53 let ci = state.current_call_info();
54 if idx > 0 {
55 let slot = ci.func + idx;
56 slot.0 < state.top_idx().0
57 } else if !is_pseudo(idx) {
58 (-idx) as u32 <= state.top_idx().0.saturating_sub(ci.func.0 + 1)
59 } else if idx == LUA_REGISTRYINDEX {
60 true
61 } else {
62 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
63 let func_val = state.get_at(ci.func);
64 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
65 let upvalues = ccl.upvalues.borrow();
66 upval_n >= 1 && upval_n <= upvalues.len()
67 } else {
68 false
69 }
70 }
71}
72
73fn index_to_value(state: &LuaState, idx: i32) -> LuaValue {
78 let ci = state.current_call_info();
79 if idx > 0 {
80 let func_idx = ci.func;
81 let slot = func_idx + idx;
82 debug_assert!(
83 idx as u32 <= ci.top.saturating_sub(func_idx + 1),
84 "unacceptable index"
85 );
86 if slot.0 >= state.top_idx().0 {
87 LuaValue::Nil
88 } else {
89 state.get_at(slot)
90 }
91 } else if !is_pseudo(idx) {
92 debug_assert!(idx != 0, "invalid index");
94 let top = state.top_idx();
95 let slot = (top.0 as i32 + idx) as u32;
96 state.get_at(slot)
97 } else if idx == LUA_REGISTRYINDEX {
98 state.registry_value()
99 } else {
100 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
102 debug_assert!(upval_n <= MAX_UPVAL as usize + 1, "upvalue index too large");
103 let func_val = state.get_at(ci.func);
104 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
105 let upvalues = ccl.upvalues.borrow();
107 if upval_n >= 1 && upval_n <= upvalues.len() {
108 upvalues[upval_n - 1].clone()
109 } else {
110 LuaValue::Nil
111 }
112 } else {
113 LuaValue::Nil
114 }
115 }
116}
117
118#[inline]
120fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
121 let ci = state.current_call_info();
122 if idx > 0 {
123 let slot = ci.func + idx;
124 debug_assert!(slot.0 < state.top_idx().0, "invalid index");
125 slot
126 } else {
127 debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
128 StackIdx((state.top_idx().0 as i32 + idx) as u32)
129 }
130}
131
132pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
135 debug_assert!(n >= 0, "negative 'n'");
136 let available = state.stack_available();
137 let res = if available > n as usize {
138 true
139 } else {
140 crate::do_::grow_stack(state, n, false).unwrap_or(false)
141 };
142 if res {
143 let needed_top = state.top_idx() + n as i32;
144 let ci_idx = state.current_ci_idx();
145 if state.get_ci(ci_idx).top.0 < needed_top.0 {
146 let live_top = state.top_idx();
147 state.get_ci_mut(ci_idx).top = needed_top;
148 state.clear_stack_range(live_top, needed_top);
149 }
150 }
151 res
152}
153
154pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
171 if n <= 0 {
172 return;
173 }
174 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
175 return;
176 }
177 let abs_top = from.top_idx().0 as i32;
178 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
179 let first_abs = abs_top - n;
180 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
181 for i in 0..n {
182 let idx = StackIdx((first_abs + i) as u32);
183 buf.push(from.get_at(idx));
184 }
185 from.set_top(StackIdx(first_abs as u32));
186 for v in buf {
187 to.push(v);
188 }
189}
190
191pub fn at_panic(
192 state: &mut LuaState,
193 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
194) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
195 let old = state.global_mut().panic;
196 state.global_mut().panic = panicf;
197 old
198}
199
200pub fn version(_state: &LuaState) -> f64 {
201 504.0
202}
203
204pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
205 if idx > 0 || is_pseudo(idx) {
207 idx
208 } else {
209 let ci = state.current_call_info();
210 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
211 }
212}
213
214pub fn get_top(state: &LuaState) -> i32 {
215 let ci = state.current_call_info();
216 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
217}
218
219pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
220 let func = state.current_call_info().func;
221 let ci_top = state.current_call_info().top;
222 if idx >= 0 {
223 debug_assert!(
224 idx as u32 <= ci_top.saturating_sub(func + 1),
225 "new top too large"
226 );
227 let new_top = func + 1 + idx as i32;
228 let old_top = state.top_idx();
229 if new_top.0 > old_top.0 {
230 for i in old_top.0..new_top.0 {
231 state.set_at(i, LuaValue::Nil);
232 }
233 }
234 state.set_top_idx(new_top);
237 } else {
238 debug_assert!(
239 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
240 "invalid new top"
241 );
242 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
243 state.set_top_idx(new_top);
245 }
246 Ok(())
247}
248
249pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
250 let level = index_to_stack_idx(state, idx);
251 state.set_at(level, LuaValue::Nil);
253 Ok(())
254}
255
256#[inline]
257fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
258 let mut lo = from.0;
259 let mut hi = to.0;
260 while lo < hi {
261 let temp = state.get_at(StackIdx(lo));
262 let hi_val = state.get_at(StackIdx(hi));
263 state.set_at(StackIdx(lo), hi_val);
264 state.set_at(StackIdx(hi), temp);
265 lo += 1;
266 hi -= 1;
267 }
268}
269
270pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
271 let t = state.top_idx() - 1;
272 let p = index_to_stack_idx(state, idx);
273 debug_assert!(
274 (n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1),
275 "invalid 'n'"
276 );
277 let m = if n >= 0 {
278 t - n
279 } else {
280 StackIdx((p.0 as i32 - n - 1) as u32)
281 };
282 reverse_segment(state, p, m);
283 reverse_segment(state, m + 1, t);
284 reverse_segment(state, p, t);
285}
286
287pub fn copy(state: &mut LuaState, fromidx: i32, toidx: i32) {
288 let fr = index_to_value(state, fromidx);
289 if is_upvalue(toidx) {
290 let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
292 let func_val = state.get_at(state.current_call_info().func);
293 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
294 let wrote = {
295 let mut upvalues = ccl.upvalues.borrow_mut();
296 if upval_n >= 1 && upval_n <= upvalues.len() {
297 upvalues[upval_n - 1] = fr.clone();
298 true
299 } else {
300 false
301 }
302 };
303 if wrote {
304 state.gc().barrier(ccl, &fr);
305 }
306 }
307 } else if toidx == LUA_REGISTRYINDEX {
308 } else {
310 let to_slot = index_to_stack_idx(state, toidx);
311 state.set_at(to_slot, fr);
312 }
313}
314
315pub fn push_value(state: &mut LuaState, idx: i32) {
316 let v = index_to_value(state, idx);
317 state.push(v);
318}
319
320impl LuaState {
325 pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
326 push_value(self, idx);
327 Ok(())
328 }
329
330 pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
331 push_value(self, idx);
332 Ok(())
333 }
334
335 pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
336 rotate(self, idx, 1);
337 Ok(())
338 }
339
340 pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
346 len(self, idx)?;
347 let l = match to_integer_x(self, -1) {
348 Some(n) => n,
349 None => {
350 return Err(LuaError::runtime(format_args!(
351 "object length is not an integer"
352 )));
353 }
354 };
355 self.pop_n(1);
356 Ok(l)
357 }
358
359 pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
366 if let Some(write_fn) = self.global().stdout_hook {
367 write_fn(msg).map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
368 return Ok(());
369 }
370
371 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
372 {
373 let _ = msg;
374 Err(LuaError::runtime(format_args!(
375 "stdout not available in this host"
376 )))
377 }
378
379 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
380 {
381 use std::io::Write;
382 let stdout = std::io::stdout();
383 let mut handle = stdout.lock();
384 handle
385 .write_all(msg)
386 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
387 handle
388 .flush()
389 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
390 Ok(())
391 }
392 }
393
394 pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
403 let abs = abs_index(self, idx);
404 let v = index_to_value(self, abs);
405 let mt: Option<GcRef<LuaTable>> = match &v {
406 LuaValue::Table(t) => t.metatable(),
407 LuaValue::UserData(u) => u.metatable(),
408 _ => self.global().mt[v.base_type() as usize].clone(),
409 };
410 if let Some(mt_ref) = mt {
411 let key = self.intern_str(b"__tostring")?;
412 let f = mt_ref.get_short_str(&key);
413 if !matches!(f, LuaValue::Nil) {
414 let func_idx = self.top_idx();
415 self.push(f);
416 self.push(v.clone());
417 if self.current_ci().is_lua_code() {
418 self.do_call(func_idx, 1)?;
419 } else {
420 self.do_call_no_yield(func_idx, 1)?;
421 }
422 let top = self.top_idx();
423 let result = self.get_at(StackIdx(top.0 - 1));
424 if let LuaValue::Str(s) = result {
425 return Ok(s.as_bytes().to_vec());
426 }
427 return Err(LuaError::runtime(format_args!(
428 "'__tostring' must return a string"
429 )));
430 }
431 }
432 let bytes: Vec<u8> = match &v {
433 LuaValue::Str(s) => {
434 let out = s.as_bytes().to_vec();
435 self.push(LuaValue::Str(s.clone()));
436 out
437 }
438 LuaValue::Int(_) | LuaValue::Float(_) => {
439 let s = crate::object::num_to_string(self, &v)?;
440 let out = s.as_bytes().to_vec();
441 self.push(LuaValue::Str(s));
442 out
443 }
444 LuaValue::Bool(b) => {
445 let lit: &[u8] = if *b { b"true" } else { b"false" };
446 let s = self.intern_str(lit)?;
447 self.push(LuaValue::Str(s));
448 lit.to_vec()
449 }
450 LuaValue::Nil => {
451 let s = self.intern_str(b"nil")?;
452 self.push(LuaValue::Str(s));
453 b"nil".to_vec()
454 }
455 _ => {
456 let kind = crate::tagmethods::obj_type_name(self, &v)?;
457 let ptr = to_pointer(self, abs).unwrap_or(0);
458 let mut buf = kind;
459 buf.extend_from_slice(b": 0x");
460 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
461 let s = self.intern_str(&buf)?;
462 self.push(LuaValue::Str(s));
463 buf
464 }
465 };
466 Ok(bytes)
467 }
468
469 pub fn top(&mut self) -> i32 {
476 get_top(self)
477 }
478
479 pub fn get_top(&mut self) -> i32 {
483 get_top(self)
484 }
485
486 pub fn type_at(&mut self, idx: i32) -> LuaType {
491 lua_type_at(self, idx)
492 }
493
494 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
500 if lua_type_at(self, arg) == LuaType::None {
501 return Err(LuaError::arg_error(arg, "value expected"));
502 }
503 Ok(())
504 }
505
506 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
516 match to_lua_string(self, arg)? {
517 Some(s) => Ok(s.as_bytes().to_vec()),
518 None => {
519 let got = index_to_value(self, arg);
520 let got_name = if lua_type_at(self, arg) == LuaType::None {
521 b"no value".to_vec()
522 } else {
523 crate::tagmethods::obj_type_name(self, &got)?
524 };
525 let extramsg = format!(
526 "string expected, got {}",
527 String::from_utf8_lossy(&got_name)
528 );
529 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
530 }
531 }
532 }
533
534 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
546 match to_integer_x(self, arg) {
547 Some(d) => Ok(d),
548 None => {
549 if is_number(self, arg) {
550 Err(crate::debug::arg_error_impl(
551 self,
552 arg,
553 b"number has no integer representation",
554 ))
555 } else {
556 let got = index_to_value(self, arg);
557 let got_name = if lua_type_at(self, arg) == LuaType::None {
558 b"no value".to_vec()
559 } else {
560 crate::tagmethods::obj_type_name(self, &got)?
561 };
562 let extramsg = format!(
563 "number expected, got {}",
564 String::from_utf8_lossy(&got_name)
565 );
566 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
567 }
568 }
569 }
570 }
571
572 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
582 match to_number_x(self, arg) {
583 Some(d) => Ok(d),
584 None => {
585 let got = index_to_value(self, arg);
586 let got_name = if lua_type_at(self, arg) == LuaType::None {
587 b"no value".to_vec()
588 } else {
589 crate::tagmethods::obj_type_name(self, &got)?
590 };
591 let extramsg = format!(
592 "number expected, got {}",
593 String::from_utf8_lossy(&got_name)
594 );
595 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
596 }
597 }
598 }
599
600 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
610 match lua_type_at(self, arg) {
611 LuaType::None | LuaType::Nil => Ok(def),
612 _ => match to_integer_x(self, arg) {
613 Some(d) => Ok(d),
614 None => {
615 if is_number(self, arg) {
616 Err(LuaError::arg_error(
617 arg,
618 "number has no integer representation",
619 ))
620 } else {
621 let got = index_to_value(self, arg);
622 Err(LuaError::type_arg_error(arg, "number", &got))
623 }
624 }
625 },
626 }
627 }
628
629 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
636 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
637 }
638
639 pub fn protected_call_k(
643 &mut self,
644 nargs: i32,
645 nresults: i32,
646 msgh: i32,
647 ctx: isize,
648 k: Option<crate::state::LuaKFunction>,
649 ) -> Result<(), LuaError> {
650 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
651 }
652
653 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
654 push_lstring(self, s)?;
655 Ok(())
656 }
657
658 pub fn push_c_closure(
659 &mut self,
660 f: fn(&mut LuaState) -> Result<usize, LuaError>,
661 n: i32,
662 ) -> Result<(), LuaError> {
663 push_cclosure(self, f, n)
664 }
665
666 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
667 raw_set_i(self, idx, n)
668 }
669
670 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
671 set_i(self, idx, n)
672 }
673
674 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
678 get_i_value(self, t, n)
679 }
680
681 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
685 set_i_value(self, t, n)
686 }
687
688 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
689 create_table(self, narr, nrec)
690 }
691
692 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
696 set_field(self, LUA_REGISTRYINDEX, key)
697 }
698
699 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
705 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
706 return Ok(false);
707 }
708 self.pop_n(1);
709 create_table(self, 0, 2)?;
710 push_lstring(self, tname)?;
711 set_field(self, -2, b"__name")?;
712 push_value(self, -1);
713 set_field(self, LUA_REGISTRYINDEX, tname)?;
714 Ok(true)
715 }
716
717 pub fn new_lib(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
724 create_table(self, 0, funcs.len() as i32)?;
725 for (name, f) in funcs {
726 push_cclosure(self, *f, 0)?;
727 set_field(self, -2, name)?;
728 }
729 Ok(())
730 }
731
732 pub fn register_lib(
738 &mut self,
739 _name: &[u8],
740 funcs: &[(&[u8], LuaCFunction)],
741 ) -> Result<(), LuaError> {
742 self.new_lib(funcs)
743 }
744
745 pub fn new_lib_table(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
754 create_table(self, 0, funcs.len() as i32)
755 }
756
757 pub fn set_funcs_with_upvalues(
762 &mut self,
763 funcs: &[(&[u8], LuaCFunction)],
764 nup: i32,
765 ) -> Result<(), LuaError> {
766 check_stack(self, nup);
767 for (name, f) in funcs {
768 for _ in 0..nup {
769 push_value(self, -nup);
770 }
771 push_cclosure(self, *f, nup)?;
772 set_field(self, -(nup + 2), name)?;
773 }
774 self.pop_n(nup as usize);
775 Ok(())
776 }
777
778 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
779 set_metatable(self, objindex)?;
780 Ok(())
781 }
782
783 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
789 get_field(self, LUA_REGISTRYINDEX, name)?;
790 set_metatable(self, -2)?;
791 Ok(())
792 }
793
794 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
798 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
799 return Ok(true);
800 }
801 self.pop_n(1);
802 let idx = abs_index(self, LUA_REGISTRYINDEX);
803 let new_tbl = self.new_table();
804 self.push(LuaValue::Table(new_tbl));
805 push_value(self, -1);
806 set_field(self, idx, name)?;
807 Ok(false)
808 }
809
810 pub fn new_userdata_typed(
820 &mut self,
821 _name: &[u8],
822 size: usize,
823 nuvalue: i32,
824 ) -> Result<GcRef<LuaUserData>, LuaError> {
825 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
826 let u = GcRef::new(LuaUserData {
828 data: vec![0u8; size].into_boxed_slice(),
829 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
830 metatable: std::cell::RefCell::new(None),
831 host_value: std::cell::RefCell::new(None),
832 });
833 u.account_buffer(u.buffer_bytes() as isize);
834 self.push(LuaValue::UserData(u.clone()));
835 self.gc().check_step();
836 Ok(u)
837 }
838}
839
840pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
843 if !is_valid_index(state, idx) {
844 return LuaType::None;
845 }
846 index_to_value(state, idx).base_type()
847}
848
849pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
850 t.type_name()
851}
852
853pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
854 let o = index_to_value(state, idx);
855 matches!(
856 o,
857 LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_))
858 )
859}
860
861pub fn is_integer(state: &LuaState, idx: i32) -> bool {
862 let o = index_to_value(state, idx);
863 matches!(o, LuaValue::Int(_))
864}
865
866pub fn is_number(state: &LuaState, idx: i32) -> bool {
867 let o = index_to_value(state, idx);
868 o.to_number_with_strconv().is_some()
869}
870
871pub fn is_string(state: &LuaState, idx: i32) -> bool {
872 let o = index_to_value(state, idx);
873 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
874}
875
876pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
877 let o = index_to_value(state, idx);
878 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
879}
880
881pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
882 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
883 return false;
884 }
885 let o1 = index_to_value(state, index1);
886 let o2 = index_to_value(state, index2);
887 state.equal_obj(None, &o1, &o2)
888}
889
890pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
892 const LUA_OPUNM: i32 = 12;
895 const LUA_OPBNOT: i32 = 14;
896 if op == LUA_OPUNM || op == LUA_OPBNOT {
897 let top_val = state.get_at(state.top_idx() - 1);
899 state.push(top_val);
900 }
901 let top = state.top_idx();
902 let a = state.get_at(top - 2);
903 let b = state.get_at(top - 1);
904 let result = state.arith_op(op, &a, &b)?;
905 state.set_at(top - 2, result);
906 state.pop();
907 Ok(())
908}
909
910pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
911 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
912 let o1 = index_to_value(state, index1);
913 let o2 = index_to_value(state, index2);
914 if valid {
915 match op {
916 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
917 1 => state.less_than(&o1, &o2),
918 2 => state.less_equal(&o1, &o2),
919 _ => {
920 debug_assert!(false, "invalid option");
921 Ok(false)
922 }
923 }
924 } else {
925 Ok(false)
926 }
927}
928
929pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
930 match state.str_to_num(s) {
932 Some((val, consumed)) => {
933 state.push(val);
934 consumed
935 }
936 None => 0,
937 }
938}
939
940pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
941 let o = index_to_value(state, idx);
942 o.to_number_with_strconv()
943}
944
945pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
946 let o = index_to_value(state, idx);
947 o.to_integer_with_strconv()
948}
949
950pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
951 let o = index_to_value(state, idx);
952 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
953}
954
955pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Option<GcRef<LuaString>>, LuaError> {
957 let o = index_to_value(state, idx);
958 if let LuaValue::Str(s) = &o {
959 return Ok(Some(s.clone()));
960 }
961 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
962 return Ok(None);
963 }
964 state.obj_to_string(idx)?;
965 state.gc().check_step();
966 let updated = index_to_value(state, idx);
967 if let LuaValue::Str(s) = updated {
968 Ok(Some(s))
969 } else {
970 Ok(None)
971 }
972}
973
974pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
975 let o = index_to_value(state, idx);
976 match &o {
977 LuaValue::Str(s) => s.len() as u64,
978 LuaValue::UserData(u) => u.len() as u64,
979 LuaValue::Table(t) => state.table_getn(t) as u64,
980 _ => 0,
981 }
982}
983
984pub fn to_cfunction(
985 state: &LuaState,
986 idx: i32,
987) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
988 let o = index_to_value(state, idx);
989 match o {
990 LuaValue::Function(LuaClosure::LightC(_f)) => None,
994 LuaValue::Function(LuaClosure::C(_ccl)) => None,
995 _ => None,
996 }
997}
998
999#[inline]
1000fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1001 match o {
1002 LuaValue::UserData(u) => {
1003 let _ = u;
1007 None
1008 }
1009 LuaValue::LightUserData(p) => Some(*p),
1010 _ => None,
1011 }
1012}
1013
1014pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1015 let o = index_to_value(state, idx);
1016 to_userdata_ptr(&o)
1017}
1018
1019pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1020 let o = index_to_value(state, idx);
1024 if let LuaValue::Thread(t) = o {
1025 Some(t)
1026 } else {
1027 None
1028 }
1029}
1030
1031pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1034 let o = index_to_value(state, idx);
1035 match &o {
1038 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1039 LuaValue::LightUserData(p) => Some(*p as usize),
1040 LuaValue::Str(s) => Some(GcRef::identity(s)),
1041 LuaValue::Table(t) => Some(GcRef::identity(t)),
1042 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1043 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1044 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1045 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1046 _ => None,
1047 }
1048}
1049
1050pub fn push_nil(state: &mut LuaState) {
1053 state.push(LuaValue::Nil);
1054}
1055
1056pub fn push_number(state: &mut LuaState, n: f64) {
1057 state.push(LuaValue::Float(n));
1058}
1059
1060pub fn push_integer(state: &mut LuaState, n: i64) {
1061 state.push(LuaValue::Int(n));
1062}
1063
1064pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1066 let ts = state.intern_str(s)?;
1067 state.push(LuaValue::Str(ts.clone()));
1068 state.gc().check_step();
1069 Ok(ts)
1070}
1071
1072pub fn push_string(
1073 state: &mut LuaState,
1074 s: Option<&[u8]>,
1075) -> Result<Option<GcRef<LuaString>>, LuaError> {
1076 match s {
1077 None => {
1078 state.push(LuaValue::Nil);
1079 state.gc().check_step();
1080 Ok(None)
1081 }
1082 Some(bytes) => {
1083 let ts = state.intern_str(bytes)?;
1084 state.push(LuaValue::Str(ts.clone()));
1085 state.gc().check_step();
1086 Ok(Some(ts))
1087 }
1088 }
1089}
1090
1091pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1095 let ts = state.intern_str(formatted)?;
1096 state.push(LuaValue::Str(ts.clone()));
1097 state.gc().check_step();
1098 Ok(ts)
1099}
1100
1101pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1103 push_vfstring(state, formatted)
1104}
1105
1106pub fn push_cclosure(
1107 state: &mut LuaState,
1108 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1109 n: i32,
1110) -> Result<(), LuaError> {
1111 let idx: lua_types::closure::LuaCFnPtr = {
1125 let mut g = state.global_mut();
1126 if n == 0 {
1127 match g.c_functions.iter().position(|existing| {
1128 existing
1129 .as_bare()
1130 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1131 }) {
1132 Some(i) => i,
1133 None => {
1134 let i = g.c_functions.len();
1135 g.c_functions.push(LuaCallable::bare(f));
1136 i
1137 }
1138 }
1139 } else {
1140 let i = g.c_functions.len();
1141 g.c_functions.push(LuaCallable::bare(f));
1142 i
1143 }
1144 };
1145 if n == 0 {
1146 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1147 } else {
1148 debug_assert!(
1149 n > 0 && (n as u32) <= MAX_UPVAL as u32,
1150 "upvalue index too large"
1151 );
1152 let n_usize = n as usize;
1153 let top = state.top_idx();
1154 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1155 let base = top.0 as usize - n_usize;
1156 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1157 for i in 0..n_usize {
1158 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1159 }
1160 state.pop_n(n_usize);
1161 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1163 func: idx,
1164 upvalues: std::cell::RefCell::new(upvalues),
1165 }));
1166 if let LuaClosure::C(ccl) = &cl {
1167 ccl.account_buffer(ccl.buffer_bytes() as isize);
1168 }
1169 state.push(LuaValue::Function(cl));
1170 state.gc().check_step();
1171 }
1172 Ok(())
1173}
1174
1175pub fn push_boolean(state: &mut LuaState, b: bool) {
1176 state.push(LuaValue::Bool(b));
1177}
1178
1179pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1180 state.push(LuaValue::LightUserData(p));
1181}
1182
1183pub fn push_thread(state: &mut LuaState) -> bool {
1185 let (value, is_main) = {
1186 let g = state.global();
1187 let id = g.current_thread_id;
1188 let v = g
1189 .thread_value_for(id)
1190 .expect("current_thread_id must always resolve to a registered thread");
1191 (v, id == g.main_thread_id)
1192 };
1193 state.push(LuaValue::Thread(value));
1194 is_main
1195}
1196
1197fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1200 let str_val = {
1201 let ts = state.intern_str(k)?;
1202 LuaValue::Str(ts)
1203 };
1204 let result = state.table_get_with_tm(&t, &str_val)?;
1207 state.push(result);
1208 let top = state.top_idx();
1209 Ok(state.get_at(top - 1).base_type())
1210}
1211
1212fn get_global_table(state: &LuaState) -> LuaValue {
1213 state.global().globals.clone()
1219}
1220
1221pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1222 let g = get_global_table(state);
1223 aux_get_str(state, g, name)
1224}
1225
1226pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1227 let t = index_to_value(state, idx);
1228 let top = state.top_idx();
1229 let key = state.get_at(top - 1);
1230 let result = state.table_get_with_tm(&t, &key)?;
1231 state.set_at(top - 1, result);
1232 let val = state.get_at(top - 1);
1233 Ok(val.base_type())
1234}
1235
1236pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1237 let t = index_to_value(state, idx);
1238 aux_get_str(state, t, k)
1239}
1240
1241pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1242 let t = index_to_value(state, idx);
1243 let key = LuaValue::Int(n);
1244 let result = state.table_get_with_tm(&t, &key)?;
1245 state.push(result);
1246 let top = state.top_idx();
1247 Ok(state.get_at(top - 1).base_type())
1248}
1249
1250pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1256 let key = LuaValue::Int(n);
1257 let result = state.table_get_with_tm(t, &key)?;
1258 state.push(result);
1259 let top = state.top_idx();
1260 Ok(state.get_at(top - 1).base_type())
1261}
1262
1263fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1264 let v = val.unwrap_or(LuaValue::Nil);
1265 state.push(v);
1266 let top = state.top_idx();
1267 state.get_at(top - 1).base_type()
1268}
1269
1270fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1271 let t = index_to_value(state, idx);
1272 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1273 if let LuaValue::Table(tbl) = t {
1274 Some(tbl)
1275 } else {
1276 None
1277 }
1278}
1279
1280pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1281 let t = get_table_value(state, idx);
1282 let top = state.top_idx();
1283 let key = state.get_at(top - 1);
1284 let val = t.as_ref().map(|tbl| tbl.get(&key));
1285 state.set_top_idx(top - 1);
1286 finish_raw_get(state, val)
1287}
1288
1289pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1290 let t = get_table_value(state, idx);
1291 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1292 finish_raw_get(state, val)
1293}
1294
1295pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1296 let t = get_table_value(state, idx);
1297 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1298 let val = t.as_ref().map(|tbl| tbl.get(&key));
1299 finish_raw_get(state, val)
1300}
1301
1302pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1303 let t = state.new_table();
1304 if narray > 0 || nrec > 0 {
1305 t.resize(state, narray as usize, nrec as usize)?;
1306 }
1307 state.push(LuaValue::Table(t));
1308 state.gc().check_step();
1309 Ok(())
1310}
1311
1312pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1313 let obj = index_to_value(state, objindex);
1314 let mt: Option<GcRef<LuaTable>> = match &obj {
1315 LuaValue::Table(t) => t.metatable(),
1316 LuaValue::UserData(u) => u.metatable(),
1317 other => {
1318 let idx = other.base_type() as usize;
1319 state.global().mt[idx].clone()
1320 }
1321 };
1322 if let Some(mt_table) = mt {
1323 state.push(LuaValue::Table(mt_table));
1324 true
1325 } else {
1326 false
1327 }
1328}
1329
1330pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1331 let o = index_to_value(state, idx);
1332 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1333 if let LuaValue::UserData(ref u) = o {
1334 let uv = u.uv.borrow();
1335 let uv_count = uv.len() as i32;
1336 if n <= 0 || n > uv_count {
1337 state.push(LuaValue::Nil);
1338 LuaType::None
1339 } else {
1340 let val = uv[(n - 1) as usize].clone();
1341 let t = val.base_type();
1342 state.push(val);
1343 t
1344 }
1345 } else {
1346 state.push(LuaValue::Nil);
1347 LuaType::None
1348 }
1349}
1350
1351fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1354 let str_val = {
1355 let ts = state.intern_str(k)?;
1356 LuaValue::Str(ts)
1357 };
1358 let top = state.top_idx();
1363 let val = state.get_at(top - 1);
1364 state.table_set_with_tm(&t, str_val, val)?;
1365 state.pop();
1366 Ok(())
1367}
1368
1369pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1370 let g = get_global_table(state);
1371 aux_set_str(state, g, name)
1372}
1373
1374pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1375 let t = index_to_value(state, idx);
1376 let top = state.top_idx();
1377 let key = state.get_at(top - 2);
1378 let val = state.get_at(top - 1);
1379 state.table_set_with_tm(&t, key, val)?;
1380 state.set_top_idx(top - 2);
1381 Ok(())
1382}
1383
1384pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1385 let t = index_to_value(state, idx);
1386 aux_set_str(state, t, k)
1387}
1388
1389pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1390 let t = index_to_value(state, idx);
1391 let top = state.top_idx();
1392 let val = state.get_at(top - 1);
1393 let key = LuaValue::Int(n);
1394 state.table_set_with_tm(&t, key, val)?;
1395 state.pop();
1396 Ok(())
1397}
1398
1399pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1405 let top = state.top_idx();
1406 let val = state.get_at(top - 1);
1407 let key = LuaValue::Int(n);
1408 state.table_set_with_tm(t, key, val)?;
1409 state.pop();
1410 Ok(())
1411}
1412
1413fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1414 let t = get_table_value(state, idx)
1415 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1416 let top = state.top_idx();
1417 let val = state.get_at(top - 1);
1418 t.raw_set(state, key, val)?;
1419 t.invalidate_tm_cache();
1420 let top_val = state.get_at(top - 1);
1421 state.gc().table_barrier_back(&t, &top_val);
1422 state.set_top_idx(top - n as i32);
1423 Ok(())
1424}
1425
1426pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1427 let top = state.top_idx();
1428 let key = state.get_at(top - 2);
1429 aux_raw_set(state, idx, key, 2)
1430}
1431
1432pub fn raw_set_p(
1433 state: &mut LuaState,
1434 idx: i32,
1435 p: *const core::ffi::c_void,
1436) -> Result<(), LuaError> {
1437 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1438 aux_raw_set(state, idx, key, 1)
1439}
1440
1441pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1442 let t = get_table_value(state, idx)
1443 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1444 let top = state.top_idx();
1445 let val = state.get_at(top - 1);
1446 t.raw_set_int(state, n, val)?;
1447 let top_val = state.get_at(top - 1);
1448 state.gc().table_barrier_back(&t, &top_val);
1449 state.pop();
1450 Ok(())
1451}
1452
1453fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1458 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1459 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1460}
1461
1462fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1463 let heap_ptr = object.heap_ptr();
1464 let mut g = state.global_mut();
1465 if g.finalizers.push_pending_unique(object) {
1466 if let Some(ptr) = heap_ptr {
1467 g.heap.move_allgc_to_finobj(ptr);
1468 }
1469 }
1470}
1471
1472pub fn run_pending_finalizers(state: &mut LuaState) {
1475 let _ = run_pending_finalizers_inner(state, false);
1476}
1477
1478const GC_FIN_MAX: usize = 10;
1479
1480pub fn run_pending_finalizers_inner(state: &mut LuaState, propagate: bool) -> Result<(), LuaError> {
1500 run_pending_finalizers_limited(state, propagate, None).map(|_| ())
1501}
1502
1503pub(crate) fn run_some_pending_finalizers_inner(
1504 state: &mut LuaState,
1505 propagate: bool,
1506) -> Result<usize, LuaError> {
1507 run_pending_finalizers_limited(state, propagate, Some(GC_FIN_MAX))
1508}
1509
1510fn run_pending_finalizers_limited(
1511 state: &mut LuaState,
1512 propagate: bool,
1513 limit: Option<usize>,
1514) -> Result<usize, LuaError> {
1515 let version = state.global().lua_version;
1516 let mut processed = 0usize;
1517 loop {
1518 if limit.map_or(false, |limit| processed >= limit) {
1519 break;
1520 }
1521 if !state.global().finalizers.has_to_be_finalized() {
1525 break;
1526 }
1527 let object = {
1537 let mut g = state.global_mut();
1538 let object = g
1539 .finalizers
1540 .pop_to_be_finalized()
1541 .expect("to-be-finalized checked non-empty");
1542 if let Some(ptr) = object.heap_ptr() {
1543 g.heap.move_tobefnz_to_allgc(ptr);
1544 }
1545 object
1546 };
1547 processed += 1;
1548 let mt = object.metatable();
1549 let gc_fn = match mt {
1550 Some(ref m) => {
1551 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1552 m.get_short_str(&name)
1553 }
1554 None => LuaValue::Nil,
1555 };
1556 if !matches!(gc_fn, LuaValue::Function(_)) {
1557 continue;
1558 }
1559 let saved_top = state.top_idx();
1560 let ci_top = state.current_call_info().top;
1561 if saved_top.0 < ci_top.0 {
1562 state.clear_stack_range(saved_top, ci_top);
1563 state.set_top(ci_top);
1564 }
1565 state.push(gc_fn);
1566 state.push(object.as_lua_value());
1567 let func_idx = state.top_idx() - 2;
1568 let _heap_guard = {
1569 let g = state.global.borrow();
1570 lua_gc::HeapGuard::push(&g.heap)
1571 };
1572 let old_allowhook = state.allowhook;
1573 let old_gcstp = state.global_mut().stop_gc_internal();
1574 state.allowhook = false;
1575 let caller_ci = state.ci;
1576 let caller_status = state.get_ci(caller_ci).callstatus;
1577 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1578 let status = crate::do_::pcall(state, |s| s.call_no_yield(func_idx, 0), func_idx, 0);
1579 let finalizer_error: Option<LuaValue> = if status != LuaStatus::Ok {
1583 Some(state.get_at(state.top_idx() - 1).clone())
1584 } else {
1585 None
1586 };
1587 state.get_ci_mut(caller_ci).callstatus = caller_status;
1588 state.allowhook = old_allowhook;
1589 state.global_mut().set_gc_stop_flags(old_gcstp);
1590 state.set_top(saved_top);
1591
1592 if let Some(errobj) = finalizer_error {
1593 match version {
1594 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1595 let msg: Vec<u8> = match &errobj {
1602 LuaValue::Str(s) => s.as_bytes().to_vec(),
1603 _ => b"no message".to_vec(),
1604 };
1605 let mut wrapped = b"error in __gc metamethod (".to_vec();
1606 wrapped.extend_from_slice(&msg);
1607 wrapped.push(b')');
1608 let interned = state.intern_str(&wrapped)?;
1609 return Err(LuaError::from_value(LuaValue::Str(interned)));
1610 }
1611 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1612 let msg: Vec<u8> = match &errobj {
1616 LuaValue::Str(s) => s.as_bytes().to_vec(),
1617 _ => b"error object is not a string".to_vec(),
1618 };
1619 state.emit_warning(b"error in ", true);
1620 state.emit_warning(b"__gc", true);
1621 state.emit_warning(b" (", true);
1622 state.emit_warning(&msg, true);
1623 state.emit_warning(b")", false);
1624 }
1625 _ => {}
1626 }
1627 }
1628 }
1629 Ok(processed)
1633}
1634
1635pub fn run_close_finalizers(state: &mut LuaState) {
1654 let pending: Vec<FinalizerObject> = state.global_mut().finalizers.take_pending();
1655 if pending.is_empty() {
1656 return;
1657 }
1658 let mut seen = std::collections::HashSet::<usize>::new();
1659 {
1660 let mut g = state.global_mut();
1661 for object in pending.into_iter().rev() {
1662 let heap_ptr = object.heap_ptr();
1663 if seen.insert(object.identity()) {
1664 g.finalizers.push_to_be_finalized(object);
1665 if let Some(ptr) = heap_ptr {
1666 g.heap.move_finobj_to_tobefnz(ptr);
1667 }
1668 }
1669 }
1670 }
1671 run_pending_finalizers(state);
1672}
1673
1674fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1680 let mut g = state.global_mut();
1681 g.weak_tables_registry.live_snapshot()
1682}
1683
1684pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1685 let top = state.top_idx();
1686 let mt_val = state.get_at(top - 1);
1687 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1688 None
1689 } else {
1690 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1691 if let LuaValue::Table(t) = mt_val {
1692 Some(t)
1693 } else {
1694 None
1695 }
1696 };
1697
1698 let obj = index_to_value(state, objindex);
1699 match obj {
1700 LuaValue::Table(ref tbl) => {
1701 if mt.is_some() {
1702 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1703 }
1704 tbl.set_metatable(mt.clone());
1705 if tbl.weak_mode() == 0 {
1706 state
1707 .global_mut()
1708 .weak_tables_registry
1709 .remove_identity(tbl.identity());
1710 } else {
1711 state
1712 .global_mut()
1713 .weak_tables_registry
1714 .push_unique(WeakTableEntry::new(tbl));
1715 }
1716 let tables_finalizable =
1726 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1727 if tables_finalizable {
1728 if let Some(ref mt_table) = mt {
1729 if metatable_has_gc(state, mt_table) {
1730 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1731 }
1732 }
1733 }
1734 }
1735 LuaValue::UserData(ref ud) => {
1736 if let Some(ref mt_table) = mt {
1737 state.gc().obj_barrier(ud, mt_table);
1738 if metatable_has_gc(state, mt_table) {
1739 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1740 }
1741 }
1742 ud.set_metatable(mt);
1743 }
1744 ref other => {
1745 let idx = other.base_type() as usize;
1746 state.global_mut().mt[idx] = mt;
1747 }
1748 }
1749 state.pop();
1750 Ok(true)
1751}
1752
1753pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1754 let o = index_to_value(state, idx);
1755 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1756 let top = state.top_idx();
1757 let val = state.get_at(top - 1);
1758 let res = if let LuaValue::UserData(ref ud) = o {
1759 let mut uv = ud.uv.borrow_mut();
1760 let nuvalue = uv.len() as i32;
1761 if n < 1 || n > nuvalue {
1762 false
1763 } else {
1764 uv[(n - 1) as usize] = val.clone();
1765 drop(uv);
1766 state.gc().barrier_back(ud, &val);
1767 true
1768 }
1769 } else {
1770 false
1771 };
1772 state.pop();
1773 Ok(res)
1774}
1775
1776pub fn call_k(
1780 state: &mut LuaState,
1781 nargs: i32,
1782 nresults: i32,
1783 ctx: isize,
1784 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1785) -> Result<(), LuaError> {
1786 let top = state.top_idx();
1787 let func_idx = top - (nargs + 1);
1788 if k.is_some() && state.is_yieldable() {
1794 let ci_idx = state.ci;
1795 {
1796 let ci = state.get_ci_mut(ci_idx);
1797 ci.set_u_c_k(k);
1798 ci.set_u_c_ctx(ctx);
1799 }
1800 state.call_at(func_idx, nresults)?;
1801 } else {
1802 state.call_no_yield(func_idx, nresults)?;
1803 }
1804 state.adjust_results(nresults);
1805 Ok(())
1806}
1807
1808pub fn pcall_k(
1810 state: &mut LuaState,
1811 nargs: i32,
1812 nresults: i32,
1813 errfunc: i32,
1814 ctx: isize,
1815 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1816) -> Result<LuaStatus, LuaError> {
1817 let _heap_guard = {
1822 let g = state.global.borrow();
1823 lua_gc::HeapGuard::push(&g.heap)
1828 };
1829 let err_handler_idx: isize = if errfunc == 0 {
1830 0
1831 } else {
1832 let o = index_to_stack_idx(state, errfunc);
1833 debug_assert!(
1834 matches!(state.get_at(o), LuaValue::Function(_)),
1835 "error handler must be a function"
1836 );
1837 o.0 as isize
1838 };
1839 let top = state.top_idx();
1840 let func_idx = top - (nargs + 1);
1841 if k.is_none() || !state.is_yieldable() {
1842 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
1843 state.adjust_results(nresults);
1844 return Ok(LuaStatus::Ok);
1845 }
1846 let ci_idx = state.ci;
1852 let allow = state.allowhook;
1853 let saved_errfunc = state.errfunc;
1854 {
1855 let ci = state.get_ci_mut(ci_idx);
1856 ci.set_u_c_k(k);
1857 ci.set_u_c_ctx(ctx);
1858 ci.set_u2_funcidx(func_idx.0 as i32);
1859 ci.set_u_c_old_errfunc(saved_errfunc);
1860 ci.set_oah(allow);
1861 ci.callstatus |= crate::state::CIST_YPCALL;
1862 }
1863 state.errfunc = err_handler_idx;
1864 let call_result = crate::do_::call(state, func_idx, nresults);
1865 match call_result {
1866 Ok(()) => {
1867 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
1870 state.errfunc = saved_errfunc;
1871 state.adjust_results(nresults);
1872 Ok(LuaStatus::Ok)
1873 }
1874 Err(crate::state::LuaError::Yield) => {
1875 Err(crate::state::LuaError::Yield)
1879 }
1880 Err(e) => {
1881 Err(e)
1885 }
1886 }
1887}
1888
1889pub fn load(
1893 state: &mut LuaState,
1894 reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
1895 chunkname: Option<&[u8]>,
1896 mode: Option<&[u8]>,
1897) -> Result<LuaStatus, LuaError> {
1898 let name = chunkname.unwrap_or(b"?");
1899 let z = crate::zio::ZIO::new(reader);
1900 let status = state.protected_parser(z, name, mode);
1901 if status == LuaStatus::Ok {
1902 let top = state.top_idx();
1903 let func_val = state.get_at(top - 1);
1904 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
1905 if !lcl.upvals.is_empty() {
1906 let gt = get_global_table(state);
1907 let uv = state.new_upval_closed(gt);
1908 lcl.set_upval(0, uv);
1909 state.gc().obj_barrier(&lcl, &uv);
1910 }
1911 }
1912 }
1913 Ok(status)
1914}
1915
1916pub fn dump(
1917 state: &LuaState,
1918 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
1919 strip: bool,
1920) -> Result<bool, LuaError> {
1921 let top = state.top_idx();
1922 let o = state.get_at(top - 1);
1923 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
1924 crate::dump::dump(state, &lcl.proto, writer, strip)?;
1925 Ok(true)
1926 } else {
1927 Ok(false)
1928 }
1929}
1930
1931pub fn status(state: &LuaState) -> LuaStatus {
1932 LuaStatus::from_raw(state.status as i32)
1933}
1934
1935#[repr(i32)]
1939#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1940pub enum GcWhat {
1941 Stop = 0,
1942 Restart = 1,
1943 Collect = 2,
1944 Count = 3,
1945 CountB = 4,
1946 Step = 5,
1947 SetPause = 6,
1948 SetStepMul = 7,
1949 IsRunning = 9,
1950 Gen = 10,
1951 Inc = 11,
1952}
1953
1954pub enum GcArgs {
1956 Stop,
1957 Restart,
1958 Collect,
1959 Count,
1960 CountB,
1961 Step {
1962 data: i32,
1963 },
1964 SetPause {
1965 value: i32,
1966 },
1967 SetStepMul {
1968 value: i32,
1969 },
1970 IsRunning,
1971 Gen {
1972 minormul: i32,
1973 majormul: i32,
1974 },
1975 Inc {
1976 pause: i32,
1977 stepmul: i32,
1978 stepsize: i32,
1979 },
1980 Param {
1983 param: usize,
1984 value: i64,
1985 },
1986}
1987
1988pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
1989 if let GcArgs::Param { param, value } = &args {
1993 return state.global_mut().gc55_param(*param, *value) as i32;
1994 }
1995 if state.global().is_gc_stopped_internally() {
1996 return -1;
1997 }
1998 match args {
1999 GcArgs::Stop => {
2000 state.global_mut().set_gc_stop_user();
2001 }
2002 GcArgs::Restart => {
2003 {
2004 let mut g = state.global_mut();
2005 crate::state::set_debt(&mut *g, 0);
2006 }
2007 state.global_mut().clear_gc_stop();
2008 }
2009 GcArgs::Collect => {
2010 if !state.allowhook {
2011 return 0;
2012 }
2013 state.gc().full_collect();
2019 if let Err(e) = run_pending_finalizers_inner(state, true) {
2027 state.global_mut().gc_finalizer_error = Some(e.into_value());
2028 }
2029 }
2030 GcArgs::Count => {
2031 let g = state.global();
2032 let total = g.heap.bytes_used();
2033 return (total >> 10) as i32;
2034 }
2035 GcArgs::CountB => {
2036 let g = state.global();
2037 let total = g.heap.bytes_used();
2038 return (total & 0x3ff) as i32;
2039 }
2040 GcArgs::Step { data } => {
2041 let old_stp = {
2042 let mut g = state.global_mut();
2043 let old = g.gc_stop_flags();
2044 g.clear_gc_stop();
2045 old
2046 };
2047 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2054 let work_units = if data == 0 {
2055 stepmul
2056 } else {
2057 let raw = (data as isize).saturating_mul(stepmul);
2058 raw.max(1)
2059 };
2060 let debt_for_result = if data == 0 {
2061 let mut g = state.global_mut();
2062 crate::state::set_debt(&mut *g, 0);
2063 0
2064 } else {
2065 let debt = data as isize * 1024 + state.global().gc_debt();
2066 let mut g = state.global_mut();
2067 crate::state::set_debt(&mut *g, debt);
2068 debt
2069 };
2070 let gen_mode = state.global().is_gen_mode();
2071 let cycle_complete = if gen_mode {
2072 if data == 0 {
2073 state.gc().generational_step_minor_only();
2074 } else {
2075 state.gc().generational_step();
2076 }
2077 if state.global().finalizers.has_to_be_finalized() {
2078 if let Err(e) = run_pending_finalizers_inner(state, true) {
2079 state.global_mut().gc_finalizer_error = Some(e.into_value());
2080 }
2081 }
2082 debt_for_result > 0 && state.global().gc_at_pause()
2083 } else if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2084 && state.global().finalizers.has_to_be_finalized()
2085 {
2086 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2087 state.global_mut().gc_finalizer_error = Some(e.into_value());
2088 }
2089 if state.global().finalizers.has_to_be_finalized() {
2090 false
2091 } else {
2092 state.global().heap.finish_callfin_phase()
2093 }
2094 } else {
2095 let completed = state.gc().incremental_step(work_units);
2096 if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2097 && state.global().finalizers.has_to_be_finalized()
2098 {
2099 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2100 state.global_mut().gc_finalizer_error = Some(e.into_value());
2101 }
2102 if state.global().finalizers.has_to_be_finalized() {
2103 false
2104 } else {
2105 state.global().heap.finish_callfin_phase()
2106 }
2107 } else {
2108 completed
2109 }
2110 };
2111 state.global_mut().set_gc_stop_flags(old_stp);
2112 {
2114 let heap_state = state.global().heap.gc_state();
2115 let mut g = state.global_mut();
2116 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2117 }
2118 return if cycle_complete { 1 } else { 0 };
2119 }
2120 GcArgs::SetPause { value } => {
2121 let old = state.global().gc_pause_param();
2122 state.global_mut().set_gc_pause_param(value);
2123 return old;
2124 }
2125 GcArgs::SetStepMul { value } => {
2126 let old = state.global().gc_stepmul_param();
2127 state.global_mut().set_gc_stepmul_param(value);
2128 return old;
2129 }
2130 GcArgs::IsRunning => {
2131 return state.global().gc_running() as i32;
2132 }
2133 GcArgs::Gen { minormul, majormul } => {
2134 let old_mode = if state.global().is_gen_mode() {
2135 10i32
2136 } else {
2137 11i32
2138 };
2139 if minormul != 0 {
2140 state.global_mut().genminormul = minormul as u8;
2141 }
2142 if majormul != 0 {
2143 state.global_mut().set_gc_genmajormul(majormul);
2144 }
2145 state.gc().change_mode(crate::state::GcKind::Generational);
2146 return old_mode;
2147 }
2148 GcArgs::Inc {
2149 pause,
2150 stepmul,
2151 stepsize,
2152 } => {
2153 let old_mode = if state.global().is_gen_mode() {
2154 10i32
2155 } else {
2156 11i32
2157 };
2158 if pause != 0 {
2159 state.global_mut().set_gc_pause_param(pause);
2160 }
2161 if stepmul != 0 {
2162 state.global_mut().set_gc_stepmul_param(stepmul);
2163 }
2164 if stepsize != 0 {
2165 state.global_mut().gcstepsize = stepsize as u8;
2166 }
2167 state.gc().change_mode(crate::state::GcKind::Incremental);
2168 return old_mode;
2169 }
2170 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2171 }
2172 0
2173}
2174
2175pub fn configure_startup_gc_mode(state: &mut LuaState) {
2182 if matches!(
2183 state.global().lua_version,
2184 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2185 ) {
2186 let _ = gc(state, GcArgs::Restart);
2187 let _ = gc(
2188 state,
2189 GcArgs::Gen {
2190 minormul: 0,
2191 majormul: 0,
2192 },
2193 );
2194 }
2195}
2196
2197pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2204 let top = state.top_idx();
2208 let errobj = state.get_at(top - 1);
2209 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2210 let memerr = state.global().memerrmsg.clone();
2211 GcRef::ptr_eq(s, &memerr)
2212 } else {
2213 false
2214 };
2215 if is_mem_err {
2216 Err(LuaError::Memory)
2217 } else {
2218 Err(LuaError::from_value(errobj))
2219 }
2220}
2221
2222pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2223 let t = get_table_value(state, idx)
2224 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2225 let top = state.top_idx();
2226 let key = state.get_at(top - 1);
2227 match t.next(key)? {
2228 Some((next_key, next_val)) => {
2229 state.set_at(top - 1, next_key);
2230 state.push(next_val);
2231 Ok(true)
2232 }
2233 None => {
2234 state.set_top_idx(top - 1);
2235 Ok(false)
2236 }
2237 }
2238}
2239
2240pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2241 let _level = index_to_stack_idx(state, idx);
2242 Ok(())
2245}
2246
2247pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2248 if n > 0 {
2249 state.concat(n)?;
2250 } else {
2251 let empty = state.intern_str(b"")?;
2252 state.push(LuaValue::Str(empty));
2253 }
2254 state.gc().check_step();
2255 Ok(())
2256}
2257
2258pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2259 let t = index_to_value(state, idx);
2260 let result = state.obj_len(&t)?;
2261 state.push(result);
2262 Ok(())
2263}
2264
2265pub fn set_warn_f(state: &mut LuaState, f: Option<Box<dyn FnMut(&[u8], bool)>>) {
2270 state.global_mut().warnf = f;
2272}
2273
2274pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2275 state.emit_warning(msg, tocont);
2276}
2277
2278pub fn new_userdata_uv(
2279 state: &mut LuaState,
2280 size: usize,
2281 nuvalue: i32,
2282) -> Result<GcRef<LuaUserData>, LuaError> {
2283 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2284 let u = state.new_userdata(size, nuvalue as usize)?;
2285 state.push(LuaValue::UserData(u.clone()));
2286 state.gc().check_step();
2287 Ok(u)
2288}
2289
2290fn aux_upvalue(state: &LuaState, fi: &LuaValue, n: i32) -> Option<(Vec<u8>, LuaValue)> {
2296 match fi {
2297 LuaValue::Function(LuaClosure::C(ccl)) => {
2298 let upvalues = ccl.upvalues.borrow();
2299 let nupvalues = upvalues.len() as i32;
2300 if n < 1 || n > nupvalues {
2301 return None;
2302 }
2303 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2304 }
2305 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2306 let nupvalues = lcl.upvals.len() as i32;
2307 if n < 1 || n > nupvalues {
2308 return None;
2309 }
2310 let val = state.upvalue_get(lcl, (n - 1) as usize);
2311 let name: Vec<u8> = lcl
2315 .proto
2316 .upvalues
2317 .get((n - 1) as usize)
2318 .and_then(|ud| ud.name.as_ref())
2319 .map(|s| s.as_bytes().to_vec())
2320 .unwrap_or_else(|| b"(no name)".to_vec());
2321 Some((name, val))
2322 }
2323 _ => None,
2324 }
2325}
2326
2327pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2328 let fi = index_to_value(state, funcindex);
2329 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2330 state.push(val);
2331 Some(name)
2332 } else {
2333 None
2334 }
2335}
2336
2337pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2338 let fi = index_to_value(state, funcindex);
2339 let (name, _) = aux_upvalue(state, &fi, n)?;
2340 let new_val = state.pop();
2341 match &fi {
2342 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2343 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2344 }
2345 LuaValue::Function(LuaClosure::C(ccl)) => {
2346 let idx = (n - 1) as usize;
2347 {
2348 let mut upvalues = ccl.upvalues.borrow_mut();
2349 if idx >= upvalues.len() {
2350 return None;
2351 }
2352 upvalues[idx] = new_val.clone();
2353 }
2354 state.gc().barrier(ccl, &new_val);
2355 }
2356 _ => return None,
2357 }
2358 Some(name)
2359}
2360
2361fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2364 let fi = index_to_value(state, fidx);
2365 debug_assert!(
2366 matches!(fi, LuaValue::Function(LuaClosure::Lua(_))),
2367 "Lua function expected"
2368 );
2369 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2370 let sizeupvalues = lcl.upvals.len() as i32;
2371 if n >= 1 && n <= sizeupvalues {
2372 Some((n - 1) as usize)
2373 } else {
2374 None
2375 }
2376 } else {
2377 None
2378 }
2379}
2380
2381pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2383 let fi = index_to_value(state, fidx);
2384 match &fi {
2385 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2386 let idx = get_upval_ref_idx(state, fidx, n)?;
2387 Some(GcRef::identity(&lcl.upval(idx)))
2389 }
2390 LuaValue::Function(LuaClosure::C(ccl)) => {
2391 let upvalues = ccl.upvalues.borrow();
2392 if n >= 1 && n <= upvalues.len() as i32 {
2393 Some(GcRef::identity(ccl) ^ (n as usize))
2396 } else {
2397 None
2398 }
2399 }
2400 LuaValue::Function(LuaClosure::LightC(_)) => None,
2401 _ => {
2402 debug_assert!(false, "function expected");
2403 None
2404 }
2405 }
2406}
2407
2408pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2410 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2411 Some(i) => i,
2412 None => return,
2413 };
2414 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2415 Some(i) => i,
2416 None => return,
2417 };
2418 let f1 = index_to_value(state, fidx1);
2419 let f2 = index_to_value(state, fidx2);
2420 if let (LuaValue::Function(LuaClosure::Lua(lcl1)), LuaValue::Function(LuaClosure::Lua(lcl2))) =
2421 (&f1, &f2)
2422 {
2423 let shared = lcl2.upval(idx2);
2424 lcl1.set_upval(idx1, shared);
2425 state.gc().obj_barrier(lcl1, &shared);
2426 }
2427}
2428
2429