1#![allow(dead_code)]
9
10use std::convert::Infallible;
11#[allow(unused_imports)] use crate::prelude::*;
12
13use crate::state::{FinalizerObject, LuaState, LuaCFunction, LuaCallable, StackIdx, WeakTableEntry,
14 LuaValueExt, LuaTypeExt, StackIdxExt,
15 LuaTableRefExt, LuaUserDataRefExt};
16use lua_types::{
17 LuaValue, LuaType, LuaError, LuaString, LuaUserData, LuaClosure,
18 GcRef, LuaStatus,
19};
20use lua_types::value::LuaTable;
21
22pub const LUA_IDENT: &[u8] =
23 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!(
94 idx != 0,
95 "invalid index"
96 );
97 let top = state.top_idx();
98 let slot = (top.0 as i32 + idx) as u32;
99 state.get_at(slot)
100 } else if idx == LUA_REGISTRYINDEX {
101 state.registry_value()
102 } else {
103 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
105 debug_assert!(upval_n <= MAX_UPVAL as usize + 1, "upvalue index too large");
106 let func_val = state.get_at(ci.func);
107 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
108 let upvalues = ccl.upvalues.borrow();
110 if upval_n >= 1 && upval_n <= upvalues.len() {
111 upvalues[upval_n - 1].clone()
112 } else {
113 LuaValue::Nil
114 }
115 } else {
116 LuaValue::Nil
117 }
118 }
119}
120
121#[inline]
123fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
124 let ci = state.current_call_info();
125 if idx > 0 {
126 let slot = ci.func + idx;
127 debug_assert!(slot.0 < state.top_idx().0, "invalid index");
128 slot
129 } else {
130 debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
131 StackIdx((state.top_idx().0 as i32 + idx) as u32)
132 }
133}
134
135pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
138 debug_assert!(n >= 0, "negative 'n'");
139 let available = state.stack_available();
140 let res = if available > n as usize {
141 true
142 } else {
143 crate::do_::grow_stack(state, n, false).unwrap_or(false)
144 };
145 if res {
146 let needed_top = state.top_idx() + n as i32;
147 let ci_idx = state.current_ci_idx();
148 if state.get_ci(ci_idx).top.0 < needed_top.0 {
149 let live_top = state.top_idx();
150 state.get_ci_mut(ci_idx).top = needed_top;
151 state.clear_stack_range(live_top, needed_top);
152 }
153 }
154 res
155}
156
157pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
174 if n <= 0 {
175 return;
176 }
177 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
178 return;
179 }
180 let abs_top = from.top_idx().0 as i32;
181 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
182 let first_abs = abs_top - n;
183 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
184 for i in 0..n {
185 let idx = StackIdx((first_abs + i) as u32);
186 buf.push(from.get_at(idx));
187 }
188 from.set_top(StackIdx(first_abs as u32));
189 for v in buf {
190 to.push(v);
191 }
192}
193
194pub fn at_panic(
195 state: &mut LuaState,
196 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
197) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
198 let old = state.global_mut().panic;
199 state.global_mut().panic = panicf;
200 old
201}
202
203pub fn version(_state: &LuaState) -> f64 {
204 504.0
205}
206
207pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
208 if idx > 0 || is_pseudo(idx) {
210 idx
211 } else {
212 let ci = state.current_call_info();
213 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
214 }
215}
216
217pub fn get_top(state: &LuaState) -> i32 {
218 let ci = state.current_call_info();
219 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
220}
221
222pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
223 let func = state.current_call_info().func;
224 let ci_top = state.current_call_info().top;
225 if idx >= 0 {
226 debug_assert!(
227 idx as u32 <= ci_top.saturating_sub(func + 1),
228 "new top too large"
229 );
230 let new_top = func + 1 + idx as i32;
231 let old_top = state.top_idx();
232 if new_top.0 > old_top.0 {
233 for i in old_top.0..new_top.0 {
234 state.set_at(i, LuaValue::Nil);
235 }
236 }
237 state.set_top_idx(new_top);
240 } else {
241 debug_assert!(
242 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
243 "invalid new top"
244 );
245 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
246 state.set_top_idx(new_top);
248 }
249 Ok(())
250}
251
252pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
253 let level = index_to_stack_idx(state, idx);
254 state.set_at(level, LuaValue::Nil);
256 Ok(())
257}
258
259#[inline]
260fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
261 let mut lo = from.0;
262 let mut hi = to.0;
263 while lo < hi {
264 let temp = state.get_at(StackIdx(lo));
265 let hi_val = state.get_at(StackIdx(hi));
266 state.set_at(StackIdx(lo), hi_val);
267 state.set_at(StackIdx(hi), temp);
268 lo += 1;
269 hi -= 1;
270 }
271}
272
273pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
274 let t = state.top_idx() - 1;
275 let p = index_to_stack_idx(state, idx);
276 debug_assert!((n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1), "invalid 'n'");
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(
724 &mut self,
725 funcs: &[(&[u8], LuaCFunction)],
726 ) -> Result<(), LuaError> {
727 create_table(self, 0, funcs.len() as i32)?;
728 for (name, f) in funcs {
729 push_cclosure(self, *f, 0)?;
730 set_field(self, -2, name)?;
731 }
732 Ok(())
733 }
734
735 pub fn register_lib(
741 &mut self,
742 _name: &[u8],
743 funcs: &[(&[u8], LuaCFunction)],
744 ) -> Result<(), LuaError> {
745 self.new_lib(funcs)
746 }
747
748 pub fn new_lib_table(
757 &mut self,
758 funcs: &[(&[u8], LuaCFunction)],
759 ) -> Result<(), LuaError> {
760 create_table(self, 0, funcs.len() as i32)
761 }
762
763 pub fn set_funcs_with_upvalues(
768 &mut self,
769 funcs: &[(&[u8], LuaCFunction)],
770 nup: i32,
771 ) -> Result<(), LuaError> {
772 check_stack(self, nup);
773 for (name, f) in funcs {
774 for _ in 0..nup {
775 push_value(self, -nup);
776 }
777 push_cclosure(self, *f, nup)?;
778 set_field(self, -(nup + 2), name)?;
779 }
780 self.pop_n(nup as usize);
781 Ok(())
782 }
783
784 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
785 set_metatable(self, objindex)?;
786 Ok(())
787 }
788
789 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
795 get_field(self, LUA_REGISTRYINDEX, name)?;
796 set_metatable(self, -2)?;
797 Ok(())
798 }
799
800 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
804 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
805 return Ok(true);
806 }
807 self.pop_n(1);
808 let idx = abs_index(self, LUA_REGISTRYINDEX);
809 let new_tbl = self.new_table();
810 self.push(LuaValue::Table(new_tbl));
811 push_value(self, -1);
812 set_field(self, idx, name)?;
813 Ok(false)
814 }
815
816 pub fn new_userdata_typed(
826 &mut self,
827 _name: &[u8],
828 size: usize,
829 nuvalue: i32,
830 ) -> Result<GcRef<LuaUserData>, LuaError> {
831 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
832 let u = GcRef::new(LuaUserData {
834 data: vec![0u8; size].into_boxed_slice(),
835 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
836 metatable: std::cell::RefCell::new(None),
837 host_value: std::cell::RefCell::new(None),
838 });
839 u.account_buffer(u.buffer_bytes() as isize);
840 self.push(LuaValue::UserData(u.clone()));
841 self.gc().check_step();
842 Ok(u)
843 }
844}
845
846pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
849 if !is_valid_index(state, idx) {
850 return LuaType::None;
851 }
852 index_to_value(state, idx).base_type()
853}
854
855pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
856 t.type_name()
857}
858
859pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
860 let o = index_to_value(state, idx);
861 matches!(o, LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_)))
862}
863
864pub fn is_integer(state: &LuaState, idx: i32) -> bool {
865 let o = index_to_value(state, idx);
866 matches!(o, LuaValue::Int(_))
867}
868
869pub fn is_number(state: &LuaState, idx: i32) -> bool {
870 let o = index_to_value(state, idx);
871 o.to_number_with_strconv().is_some()
872}
873
874pub fn is_string(state: &LuaState, idx: i32) -> bool {
875 let o = index_to_value(state, idx);
876 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
877}
878
879pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
880 let o = index_to_value(state, idx);
881 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
882}
883
884pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
885 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
886 return false;
887 }
888 let o1 = index_to_value(state, index1);
889 let o2 = index_to_value(state, index2);
890 state.equal_obj(None, &o1, &o2)
891}
892
893pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
895 const LUA_OPUNM: i32 = 12;
898 const LUA_OPBNOT: i32 = 14;
899 if op == LUA_OPUNM || op == LUA_OPBNOT {
900 let top_val = state.get_at(state.top_idx() - 1);
902 state.push(top_val);
903 }
904 let top = state.top_idx();
905 let a = state.get_at(top - 2);
906 let b = state.get_at(top - 1);
907 let result = state.arith_op(op, &a, &b)?;
908 state.set_at(top - 2, result);
909 state.pop();
910 Ok(())
911}
912
913pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
914 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
915 let o1 = index_to_value(state, index1);
916 let o2 = index_to_value(state, index2);
917 if valid {
918 match op {
919 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
920 1 => state.less_than(&o1, &o2),
921 2 => state.less_equal(&o1, &o2),
922 _ => {
923 debug_assert!(false, "invalid option");
924 Ok(false)
925 }
926 }
927 } else {
928 Ok(false)
929 }
930}
931
932pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
933 match state.str_to_num(s) {
935 Some((val, consumed)) => {
936 state.push(val);
937 consumed
938 }
939 None => 0,
940 }
941}
942
943pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
944 let o = index_to_value(state, idx);
945 o.to_number_with_strconv()
946}
947
948pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
949 let o = index_to_value(state, idx);
950 o.to_integer_with_strconv()
951}
952
953pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
954 let o = index_to_value(state, idx);
955 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
956}
957
958pub fn to_lua_string(
960 state: &mut LuaState,
961 idx: i32,
962) -> Result<Option<GcRef<LuaString>>, LuaError> {
963 let o = index_to_value(state, idx);
964 if let LuaValue::Str(s) = &o {
965 return Ok(Some(s.clone()));
966 }
967 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
968 return Ok(None);
969 }
970 state.obj_to_string(idx)?;
971 state.gc().check_step();
972 let updated = index_to_value(state, idx);
973 if let LuaValue::Str(s) = updated {
974 Ok(Some(s))
975 } else {
976 Ok(None)
977 }
978}
979
980pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
981 let o = index_to_value(state, idx);
982 match &o {
983 LuaValue::Str(s) => s.len() as u64,
984 LuaValue::UserData(u) => u.len() as u64,
985 LuaValue::Table(t) => state.table_getn(t) as u64,
986 _ => 0,
987 }
988}
989
990pub fn to_cfunction(
991 state: &LuaState,
992 idx: i32,
993) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
994 let o = index_to_value(state, idx);
995 match o {
996 LuaValue::Function(LuaClosure::LightC(_f)) => None,
1000 LuaValue::Function(LuaClosure::C(_ccl)) => None,
1001 _ => None,
1002 }
1003}
1004
1005#[inline]
1006fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1007 match o {
1008 LuaValue::UserData(u) => {
1009 let _ = u;
1013 None
1014 }
1015 LuaValue::LightUserData(p) => Some(*p),
1016 _ => None,
1017 }
1018}
1019
1020pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1021 let o = index_to_value(state, idx);
1022 to_userdata_ptr(&o)
1023}
1024
1025pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1026 let o = index_to_value(state, idx);
1030 if let LuaValue::Thread(t) = o {
1031 Some(t)
1032 } else {
1033 None
1034 }
1035}
1036
1037pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1040 let o = index_to_value(state, idx);
1041 match &o {
1044 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1045 LuaValue::LightUserData(p) => Some(*p as usize),
1046 LuaValue::Str(s) => Some(GcRef::identity(s)),
1047 LuaValue::Table(t) => Some(GcRef::identity(t)),
1048 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1049 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1050 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1051 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1052 _ => None,
1053 }
1054}
1055
1056pub fn push_nil(state: &mut LuaState) {
1059 state.push(LuaValue::Nil);
1060}
1061
1062pub fn push_number(state: &mut LuaState, n: f64) {
1063 state.push(LuaValue::Float(n));
1064}
1065
1066pub fn push_integer(state: &mut LuaState, n: i64) {
1067 state.push(LuaValue::Int(n));
1068}
1069
1070pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1072 let ts = state.intern_str(s)?;
1073 state.push(LuaValue::Str(ts.clone()));
1074 state.gc().check_step();
1075 Ok(ts)
1076}
1077
1078pub fn push_string(state: &mut LuaState, s: Option<&[u8]>) -> Result<Option<GcRef<LuaString>>, LuaError> {
1079 match s {
1080 None => {
1081 state.push(LuaValue::Nil);
1082 state.gc().check_step();
1083 Ok(None)
1084 }
1085 Some(bytes) => {
1086 let ts = state.intern_str(bytes)?;
1087 state.push(LuaValue::Str(ts.clone()));
1088 state.gc().check_step();
1089 Ok(Some(ts))
1090 }
1091 }
1092}
1093
1094pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1098 let ts = state.intern_str(formatted)?;
1099 state.push(LuaValue::Str(ts.clone()));
1100 state.gc().check_step();
1101 Ok(ts)
1102}
1103
1104pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1106 push_vfstring(state, formatted)
1107}
1108
1109pub fn push_cclosure(
1110 state: &mut LuaState,
1111 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1112 n: i32,
1113) -> Result<(), LuaError> {
1114 let idx: lua_types::closure::LuaCFnPtr = {
1128 let mut g = state.global_mut();
1129 if n == 0 {
1130 match g.c_functions.iter().position(|existing| {
1131 existing
1132 .as_bare()
1133 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1134 }) {
1135 Some(i) => i,
1136 None => {
1137 let i = g.c_functions.len();
1138 g.c_functions.push(LuaCallable::bare(f));
1139 i
1140 }
1141 }
1142 } else {
1143 let i = g.c_functions.len();
1144 g.c_functions.push(LuaCallable::bare(f));
1145 i
1146 }
1147 };
1148 if n == 0 {
1149 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1150 } else {
1151 debug_assert!(n > 0 && (n as u32) <= MAX_UPVAL as u32, "upvalue index too large");
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().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(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> Result<(), LuaError> {
1433 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1434 aux_raw_set(state, idx, key, 1)
1435}
1436
1437pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1438 let t = get_table_value(state, idx)
1439 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1440 let top = state.top_idx();
1441 let val = state.get_at(top - 1);
1442 t.raw_set_int(state, n, val)?;
1443 let top_val = state.get_at(top - 1);
1444 state.gc().barrier_back(&t, &top_val);
1445 state.pop();
1446 Ok(())
1447}
1448
1449fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1454 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1455 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1456}
1457
1458fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1459 let heap_ptr = object.heap_ptr();
1460 let mut g = state.global_mut();
1461 if g.finalizers.push_pending_unique(object) {
1462 if let Some(ptr) = heap_ptr {
1463 g.heap.move_allgc_to_finobj(ptr);
1464 }
1465 }
1466}
1467
1468pub fn run_pending_finalizers(state: &mut LuaState) {
1471 let _ = run_pending_finalizers_inner(state, false);
1472}
1473
1474const GC_FIN_MAX: usize = 10;
1475
1476pub fn run_pending_finalizers_inner(
1496 state: &mut LuaState,
1497 propagate: bool,
1498) -> Result<(), LuaError> {
1499 run_pending_finalizers_limited(state, propagate, None).map(|_| ())
1500}
1501
1502pub(crate) fn run_some_pending_finalizers_inner(
1503 state: &mut LuaState,
1504 propagate: bool,
1505) -> Result<usize, LuaError> {
1506 run_pending_finalizers_limited(state, propagate, Some(GC_FIN_MAX))
1507}
1508
1509fn run_pending_finalizers_limited(
1510 state: &mut LuaState,
1511 propagate: bool,
1512 limit: Option<usize>,
1513) -> Result<usize, LuaError> {
1514 let version = state.global().lua_version;
1515 let mut processed = 0usize;
1516 loop {
1517 if limit.map_or(false, |limit| processed >= limit) {
1518 break;
1519 }
1520 if !state.global().finalizers.has_to_be_finalized() {
1524 break;
1525 }
1526 let object = {
1536 let mut g = state.global_mut();
1537 let object = g
1538 .finalizers
1539 .pop_to_be_finalized()
1540 .expect("to-be-finalized checked non-empty");
1541 if let Some(ptr) = object.heap_ptr() {
1542 g.heap.move_tobefnz_to_allgc(ptr);
1543 }
1544 object
1545 };
1546 processed += 1;
1547 let mt = object.metatable();
1548 let gc_fn = match mt {
1549 Some(ref m) => {
1550 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1551 m.get_short_str(&name)
1552 }
1553 None => LuaValue::Nil,
1554 };
1555 if !matches!(gc_fn, LuaValue::Function(_)) {
1556 continue;
1557 }
1558 let saved_top = state.top_idx();
1559 let ci_top = state.current_call_info().top;
1560 if saved_top.0 < ci_top.0 {
1561 state.clear_stack_range(saved_top, ci_top);
1562 state.set_top(ci_top);
1563 }
1564 state.push(gc_fn);
1565 state.push(object.as_lua_value());
1566 let func_idx = state.top_idx() - 2;
1567 let _heap_guard = {
1568 let g = state.global.borrow();
1569 lua_gc::HeapGuard::push(&g.heap)
1570 };
1571 let old_allowhook = state.allowhook;
1572 let old_gcstp = state.global_mut().stop_gc_internal();
1573 state.allowhook = false;
1574 let caller_ci = state.ci;
1575 let caller_status = state.get_ci(caller_ci).callstatus;
1576 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1577 let status = crate::do_::pcall(
1578 state,
1579 |s| s.call_no_yield(func_idx, 0),
1580 func_idx,
1581 0,
1582 );
1583 let finalizer_error: Option<LuaValue> =
1587 if status != LuaStatus::Ok {
1588 Some(state.get_at(state.top_idx() - 1).clone())
1589 } else {
1590 None
1591 };
1592 state.get_ci_mut(caller_ci).callstatus = caller_status;
1593 state.allowhook = old_allowhook;
1594 state.global_mut().set_gc_stop_flags(old_gcstp);
1595 state.set_top(saved_top);
1596
1597 if let Some(errobj) = finalizer_error {
1598 match version {
1599 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1600 let msg: Vec<u8> = match &errobj {
1607 LuaValue::Str(s) => s.as_bytes().to_vec(),
1608 _ => b"no message".to_vec(),
1609 };
1610 let mut wrapped = b"error in __gc metamethod (".to_vec();
1611 wrapped.extend_from_slice(&msg);
1612 wrapped.push(b')');
1613 let interned = state.intern_str(&wrapped)?;
1614 return Err(LuaError::from_value(LuaValue::Str(interned)));
1615 }
1616 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1617 let msg: Vec<u8> = match &errobj {
1621 LuaValue::Str(s) => s.as_bytes().to_vec(),
1622 _ => b"error object is not a string".to_vec(),
1623 };
1624 state.emit_warning(b"error in ", true);
1625 state.emit_warning(b"__gc", true);
1626 state.emit_warning(b" (", true);
1627 state.emit_warning(&msg, true);
1628 state.emit_warning(b")", false);
1629 }
1630 _ => {}
1631 }
1632 }
1633 }
1634 Ok(processed)
1638}
1639
1640pub fn run_close_finalizers(state: &mut LuaState) {
1659 let pending: Vec<FinalizerObject> = state.global_mut().finalizers.take_pending();
1660 if pending.is_empty() {
1661 return;
1662 }
1663 let mut seen = std::collections::HashSet::<usize>::new();
1664 {
1665 let mut g = state.global_mut();
1666 for object in pending.into_iter().rev() {
1667 let heap_ptr = object.heap_ptr();
1668 if seen.insert(object.identity()) {
1669 g.finalizers.push_to_be_finalized(object);
1670 if let Some(ptr) = heap_ptr {
1671 g.heap.move_finobj_to_tobefnz(ptr);
1672 }
1673 }
1674 }
1675 }
1676 run_pending_finalizers(state);
1677}
1678
1679fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1685 let mut g = state.global_mut();
1686 g.weak_tables_registry.live_snapshot()
1687}
1688
1689pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1690 let top = state.top_idx();
1691 let mt_val = state.get_at(top - 1);
1692 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1693 None
1694 } else {
1695 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1696 if let LuaValue::Table(t) = mt_val {
1697 Some(t)
1698 } else {
1699 None
1700 }
1701 };
1702
1703 let obj = index_to_value(state, objindex);
1704 match obj {
1705 LuaValue::Table(ref tbl) => {
1706 if mt.is_some() {
1707 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1708 }
1709 tbl.set_metatable(mt.clone());
1710 if tbl.weak_mode() == 0 {
1711 state
1712 .global_mut()
1713 .weak_tables_registry
1714 .remove_identity(tbl.identity());
1715 } else {
1716 state
1717 .global_mut()
1718 .weak_tables_registry
1719 .push_unique(WeakTableEntry::new(tbl));
1720 }
1721 let tables_finalizable =
1731 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1732 if tables_finalizable {
1733 if let Some(ref mt_table) = mt {
1734 if metatable_has_gc(state, mt_table) {
1735 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1736 }
1737 }
1738 }
1739 }
1740 LuaValue::UserData(ref ud) => {
1741 if let Some(ref mt_table) = mt {
1742 state.gc().obj_barrier(ud, mt_table);
1743 if metatable_has_gc(state, mt_table) {
1744 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1745 }
1746 }
1747 ud.set_metatable(mt);
1748 }
1749 ref other => {
1750 let idx = other.base_type() as usize;
1751 state.global_mut().mt[idx] = mt;
1752 }
1753 }
1754 state.pop();
1755 Ok(true)
1756}
1757
1758pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1759 let o = index_to_value(state, idx);
1760 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1761 let top = state.top_idx();
1762 let val = state.get_at(top - 1);
1763 let res = if let LuaValue::UserData(ref ud) = o {
1764 let mut uv = ud.uv.borrow_mut();
1765 let nuvalue = uv.len() as i32;
1766 if n < 1 || n > nuvalue {
1767 false
1768 } else {
1769 uv[(n - 1) as usize] = val.clone();
1770 drop(uv);
1771 state.gc().barrier_back(ud, &val);
1772 true
1773 }
1774 } else {
1775 false
1776 };
1777 state.pop();
1778 Ok(res)
1779}
1780
1781pub fn call_k(
1785 state: &mut LuaState,
1786 nargs: i32,
1787 nresults: i32,
1788 ctx: isize,
1789 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1790) -> Result<(), LuaError> {
1791 let top = state.top_idx();
1792 let func_idx = top - (nargs + 1);
1793 if k.is_some() && state.is_yieldable() {
1799 let ci_idx = state.ci;
1800 {
1801 let ci = state.get_ci_mut(ci_idx);
1802 ci.set_u_c_k(k);
1803 ci.set_u_c_ctx(ctx);
1804 }
1805 state.call_at(func_idx, nresults)?;
1806 } else {
1807 state.call_no_yield(func_idx, nresults)?;
1808 }
1809 state.adjust_results(nresults);
1810 Ok(())
1811}
1812
1813pub fn pcall_k(
1815 state: &mut LuaState,
1816 nargs: i32,
1817 nresults: i32,
1818 errfunc: i32,
1819 ctx: isize,
1820 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1821) -> Result<LuaStatus, LuaError> {
1822 let _heap_guard = {
1827 let g = state.global.borrow();
1828 lua_gc::HeapGuard::push(&g.heap)
1833 };
1834 let err_handler_idx: isize = if errfunc == 0 {
1835 0
1836 } else {
1837 let o = index_to_stack_idx(state, errfunc);
1838 debug_assert!(
1839 matches!(state.get_at(o), LuaValue::Function(_)),
1840 "error handler must be a function"
1841 );
1842 o.0 as isize
1843 };
1844 let top = state.top_idx();
1845 let func_idx = top - (nargs + 1);
1846 if k.is_none() || !state.is_yieldable() {
1847 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
1848 state.adjust_results(nresults);
1849 return Ok(LuaStatus::Ok);
1850 }
1851 let ci_idx = state.ci;
1857 let allow = state.allowhook;
1858 let saved_errfunc = state.errfunc;
1859 {
1860 let ci = state.get_ci_mut(ci_idx);
1861 ci.set_u_c_k(k);
1862 ci.set_u_c_ctx(ctx);
1863 ci.set_u2_funcidx(func_idx.0 as i32);
1864 ci.set_u_c_old_errfunc(saved_errfunc);
1865 ci.set_oah(allow);
1866 ci.callstatus |= crate::state::CIST_YPCALL;
1867 }
1868 state.errfunc = err_handler_idx;
1869 let call_result = crate::do_::call(state, func_idx, nresults);
1870 match call_result {
1871 Ok(()) => {
1872 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
1875 state.errfunc = saved_errfunc;
1876 state.adjust_results(nresults);
1877 Ok(LuaStatus::Ok)
1878 }
1879 Err(crate::state::LuaError::Yield) => {
1880 Err(crate::state::LuaError::Yield)
1884 }
1885 Err(e) => {
1886 Err(e)
1890 }
1891 }
1892}
1893
1894pub fn load(
1898 state: &mut LuaState,
1899 reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
1900 chunkname: Option<&[u8]>,
1901 mode: Option<&[u8]>,
1902) -> Result<LuaStatus, LuaError> {
1903 let name = chunkname.unwrap_or(b"?");
1904 let z = crate::zio::ZIO::new(reader);
1905 let status = state.protected_parser(z, name, mode);
1906 if status == LuaStatus::Ok {
1907 let top = state.top_idx();
1908 let func_val = state.get_at(top - 1);
1909 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
1910 if !lcl.upvals.is_empty() {
1911 let gt = get_global_table(state);
1912 let uv = state.new_upval_closed(gt);
1913 lcl.set_upval(0, uv);
1914 state.gc().obj_barrier(&lcl, &uv);
1915 }
1916 }
1917 }
1918 Ok(status)
1919}
1920
1921pub fn dump(
1922 state: &LuaState,
1923 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
1924 strip: bool,
1925) -> Result<bool, LuaError> {
1926 let top = state.top_idx();
1927 let o = state.get_at(top - 1);
1928 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
1929 crate::dump::dump(state, &lcl.proto, writer, strip)?;
1930 Ok(true)
1931 } else {
1932 Ok(false)
1933 }
1934}
1935
1936pub fn status(state: &LuaState) -> LuaStatus {
1937 LuaStatus::from_raw(state.status as i32)
1938}
1939
1940#[repr(i32)]
1944#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1945pub enum GcWhat {
1946 Stop = 0,
1947 Restart = 1,
1948 Collect = 2,
1949 Count = 3,
1950 CountB = 4,
1951 Step = 5,
1952 SetPause = 6,
1953 SetStepMul = 7,
1954 IsRunning = 9,
1955 Gen = 10,
1956 Inc = 11,
1957}
1958
1959pub enum GcArgs {
1961 Stop,
1962 Restart,
1963 Collect,
1964 Count,
1965 CountB,
1966 Step { data: i32 },
1967 SetPause { value: i32 },
1968 SetStepMul { value: i32 },
1969 IsRunning,
1970 Gen { minormul: i32, majormul: i32 },
1971 Inc { pause: i32, stepmul: i32, stepsize: i32 },
1972 Param { param: usize, value: i64 },
1975}
1976
1977pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
1978 if let GcArgs::Param { param, value } = &args {
1982 return state.global_mut().gc55_param(*param, *value) as i32;
1983 }
1984 if state.global().is_gc_stopped_internally() {
1985 return -1;
1986 }
1987 match args {
1988 GcArgs::Stop => {
1989 state.global_mut().set_gc_stop_user();
1990 }
1991 GcArgs::Restart => {
1992 {
1993 let mut g = state.global_mut();
1994 crate::state::set_debt(&mut *g, 0);
1995 }
1996 state.global_mut().clear_gc_stop();
1997 }
1998 GcArgs::Collect => {
1999 if !state.allowhook {
2000 return 0;
2001 }
2002 state.gc().full_collect();
2008 if let Err(e) = run_pending_finalizers_inner(state, true) {
2016 state.global_mut().gc_finalizer_error = Some(e.into_value());
2017 }
2018 }
2019 GcArgs::Count => {
2020 let g = state.global();
2021 let total = g.heap.bytes_used();
2022 return (total >> 10) as i32;
2023 }
2024 GcArgs::CountB => {
2025 let g = state.global();
2026 let total = g.heap.bytes_used();
2027 return (total & 0x3ff) as i32;
2028 }
2029 GcArgs::Step { data } => {
2030 let old_stp = {
2031 let mut g = state.global_mut();
2032 let old = g.gc_stop_flags();
2033 g.clear_gc_stop();
2034 old
2035 };
2036 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2043 let work_units = if data == 0 {
2044 stepmul
2045 } else {
2046 let raw = (data as isize).saturating_mul(stepmul);
2047 raw.max(1)
2048 };
2049 let debt_for_result = if data == 0 {
2050 let mut g = state.global_mut();
2051 crate::state::set_debt(&mut *g, 0);
2052 0
2053 } else {
2054 let debt = data as isize * 1024 + state.global().gc_debt();
2055 let mut g = state.global_mut();
2056 crate::state::set_debt(&mut *g, debt);
2057 debt
2058 };
2059 let gen_mode = state.global().is_gen_mode();
2060 let cycle_complete = if gen_mode {
2061 if data == 0 {
2062 state.gc().generational_step_minor_only();
2063 } else {
2064 state.gc().generational_step();
2065 }
2066 if state.global().finalizers.has_to_be_finalized() {
2067 if let Err(e) = run_pending_finalizers_inner(state, true) {
2068 state.global_mut().gc_finalizer_error = Some(e.into_value());
2069 }
2070 }
2071 debt_for_result > 0 && state.global().gc_at_pause()
2072 } else if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2073 && state.global().finalizers.has_to_be_finalized()
2074 {
2075 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2076 state.global_mut().gc_finalizer_error = Some(e.into_value());
2077 }
2078 if state.global().finalizers.has_to_be_finalized() {
2079 false
2080 } else {
2081 state.global().heap.finish_callfin_phase()
2082 }
2083 } else {
2084 let completed = state.gc().incremental_step(work_units);
2085 if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2086 && state.global().finalizers.has_to_be_finalized()
2087 {
2088 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2089 state.global_mut().gc_finalizer_error = Some(e.into_value());
2090 }
2091 if state.global().finalizers.has_to_be_finalized() {
2092 false
2093 } else {
2094 state.global().heap.finish_callfin_phase()
2095 }
2096 } else {
2097 completed
2098 }
2099 };
2100 state.global_mut().set_gc_stop_flags(old_stp);
2101 {
2103 let heap_state = state.global().heap.gc_state();
2104 let mut g = state.global_mut();
2105 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2106 }
2107 return if cycle_complete { 1 } else { 0 };
2108 }
2109 GcArgs::SetPause { value } => {
2110 let old = state.global().gc_pause_param();
2111 state.global_mut().set_gc_pause_param(value);
2112 return old;
2113 }
2114 GcArgs::SetStepMul { value } => {
2115 let old = state.global().gc_stepmul_param();
2116 state.global_mut().set_gc_stepmul_param(value);
2117 return old;
2118 }
2119 GcArgs::IsRunning => {
2120 return state.global().gc_running() as i32;
2121 }
2122 GcArgs::Gen { minormul, majormul } => {
2123 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2124 if minormul != 0 {
2125 state.global_mut().genminormul = minormul as u8;
2126 }
2127 if majormul != 0 {
2128 state.global_mut().set_gc_genmajormul(majormul);
2129 }
2130 state.gc().change_mode(crate::state::GcKind::Generational);
2131 return old_mode;
2132 }
2133 GcArgs::Inc { pause, stepmul, stepsize } => {
2134 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2135 if pause != 0 {
2136 state.global_mut().set_gc_pause_param(pause);
2137 }
2138 if stepmul != 0 {
2139 state.global_mut().set_gc_stepmul_param(stepmul);
2140 }
2141 if stepsize != 0 {
2142 state.global_mut().gcstepsize = stepsize as u8;
2143 }
2144 state.gc().change_mode(crate::state::GcKind::Incremental);
2145 return old_mode;
2146 }
2147 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2148 }
2149 0
2150}
2151
2152pub fn configure_startup_gc_mode(state: &mut LuaState) {
2159 if matches!(
2160 state.global().lua_version,
2161 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2162 ) {
2163 let _ = gc(state, GcArgs::Restart);
2164 let _ = gc(
2165 state,
2166 GcArgs::Gen {
2167 minormul: 0,
2168 majormul: 0,
2169 },
2170 );
2171 }
2172}
2173
2174pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2181 let top = state.top_idx();
2185 let errobj = state.get_at(top - 1);
2186 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2187 let memerr = state.global().memerrmsg.clone();
2188 GcRef::ptr_eq(s, &memerr)
2189 } else {
2190 false
2191 };
2192 if is_mem_err {
2193 Err(LuaError::Memory)
2194 } else {
2195 Err(LuaError::from_value(errobj))
2196 }
2197}
2198
2199pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2200 let t = get_table_value(state, idx)
2201 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2202 let top = state.top_idx();
2203 let key = state.get_at(top - 1);
2204 match t.next(key)? {
2205 Some((next_key, next_val)) => {
2206 state.set_at(top - 1, next_key);
2207 state.push(next_val);
2208 Ok(true)
2209 }
2210 None => {
2211 state.set_top_idx(top - 1);
2212 Ok(false)
2213 }
2214 }
2215}
2216
2217pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2218 let _level = index_to_stack_idx(state, idx);
2219 Ok(())
2222}
2223
2224pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2225 if n > 0 {
2226 state.concat(n)?;
2227 } else {
2228 let empty = state.intern_str(b"")?;
2229 state.push(LuaValue::Str(empty));
2230 }
2231 state.gc().check_step();
2232 Ok(())
2233}
2234
2235pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2236 let t = index_to_value(state, idx);
2237 let result = state.obj_len(&t)?;
2238 state.push(result);
2239 Ok(())
2240}
2241
2242pub fn set_warn_f(
2247 state: &mut LuaState,
2248 f: Option<Box<dyn FnMut(&[u8], bool)>>,
2249) {
2250 state.global_mut().warnf = f;
2252}
2253
2254pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2255 state.emit_warning(msg, tocont);
2256}
2257
2258pub fn new_userdata_uv(
2259 state: &mut LuaState,
2260 size: usize,
2261 nuvalue: i32,
2262) -> Result<GcRef<LuaUserData>, LuaError> {
2263 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2264 let u = state.new_userdata(size, nuvalue as usize)?;
2265 state.push(LuaValue::UserData(u.clone()));
2266 state.gc().check_step();
2267 Ok(u)
2268}
2269
2270fn aux_upvalue(
2276 state: &LuaState,
2277 fi: &LuaValue,
2278 n: i32,
2279) -> Option<(Vec<u8>, LuaValue)> {
2280 match fi {
2281 LuaValue::Function(LuaClosure::C(ccl)) => {
2282 let upvalues = ccl.upvalues.borrow();
2283 let nupvalues = upvalues.len() as i32;
2284 if n < 1 || n > nupvalues {
2285 return None;
2286 }
2287 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2288 }
2289 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2290 let nupvalues = lcl.upvals.len() as i32;
2291 if n < 1 || n > nupvalues {
2292 return None;
2293 }
2294 let val = state.upvalue_get(lcl, (n - 1) as usize);
2295 let name: Vec<u8> = lcl
2299 .proto
2300 .upvalues
2301 .get((n - 1) as usize)
2302 .and_then(|ud| ud.name.as_ref())
2303 .map(|s| s.as_bytes().to_vec())
2304 .unwrap_or_else(|| b"(no name)".to_vec());
2305 Some((name, val))
2306 }
2307 _ => None,
2308 }
2309}
2310
2311pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2312 let fi = index_to_value(state, funcindex);
2313 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2314 state.push(val);
2315 Some(name)
2316 } else {
2317 None
2318 }
2319}
2320
2321pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2322 let fi = index_to_value(state, funcindex);
2323 let (name, _) = aux_upvalue(state, &fi, n)?;
2324 let new_val = state.pop();
2325 match &fi {
2326 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2327 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2328 }
2329 LuaValue::Function(LuaClosure::C(ccl)) => {
2330 let idx = (n - 1) as usize;
2331 {
2332 let mut upvalues = ccl.upvalues.borrow_mut();
2333 if idx >= upvalues.len() {
2334 return None;
2335 }
2336 upvalues[idx] = new_val.clone();
2337 }
2338 state.gc().barrier(ccl, &new_val);
2339 }
2340 _ => return None,
2341 }
2342 Some(name)
2343}
2344
2345fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2348 let fi = index_to_value(state, fidx);
2349 debug_assert!(matches!(fi, LuaValue::Function(LuaClosure::Lua(_))), "Lua function expected");
2350 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2351 let sizeupvalues = lcl.upvals.len() as i32;
2352 if n >= 1 && n <= sizeupvalues {
2353 Some((n - 1) as usize)
2354 } else {
2355 None
2356 }
2357 } else {
2358 None
2359 }
2360}
2361
2362pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2364 let fi = index_to_value(state, fidx);
2365 match &fi {
2366 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2367 let idx = get_upval_ref_idx(state, fidx, n)?;
2368 Some(GcRef::identity(&lcl.upval(idx)))
2370 }
2371 LuaValue::Function(LuaClosure::C(ccl)) => {
2372 let upvalues = ccl.upvalues.borrow();
2373 if n >= 1 && n <= upvalues.len() as i32 {
2374 Some(GcRef::identity(ccl) ^ (n as usize))
2377 } else {
2378 None
2379 }
2380 }
2381 LuaValue::Function(LuaClosure::LightC(_)) => None,
2382 _ => {
2383 debug_assert!(false, "function expected");
2384 None
2385 }
2386 }
2387}
2388
2389pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2391 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2392 Some(i) => i,
2393 None => return,
2394 };
2395 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2396 Some(i) => i,
2397 None => return,
2398 };
2399 let f1 = index_to_value(state, fidx1);
2400 let f2 = index_to_value(state, fidx2);
2401 if let (
2402 LuaValue::Function(LuaClosure::Lua(lcl1)),
2403 LuaValue::Function(LuaClosure::Lua(lcl2)),
2404 ) = (&f1, &f2)
2405 {
2406 let shared = lcl2.upval(idx2);
2407 lcl1.set_upval(idx1, shared);
2408 state.gc().obj_barrier(lcl1, &shared);
2409 }
2410}
2411
2412