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(always)]
125pub fn positive_index_value(state: &LuaState, idx: i32) -> LuaValue {
126 debug_assert!(idx > 0, "positive_index_value requires a positive index");
127 let ci = state.current_call_info();
128 let func_idx = ci.func;
129 let slot = func_idx + idx;
130 debug_assert!(
131 idx as u32 <= ci.top.saturating_sub(func_idx + 1),
132 "unacceptable index"
133 );
134 if slot.0 >= state.top_idx().0 {
135 LuaValue::Nil
136 } else {
137 state.get_at(slot)
138 }
139}
140
141#[inline]
143fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
144 let ci = state.current_call_info();
145 if idx > 0 {
146 let slot = ci.func + idx;
147 debug_assert!(slot.0 < state.top_idx().0, "invalid index");
148 slot
149 } else {
150 debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
151 StackIdx((state.top_idx().0 as i32 + idx) as u32)
152 }
153}
154
155pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
158 debug_assert!(n >= 0, "negative 'n'");
159 let available = state.stack_available();
160 let res = if available > n as usize {
161 true
162 } else {
163 crate::do_::grow_stack(state, n, false).unwrap_or(false)
164 };
165 if res {
166 let needed_top = state.top_idx() + n as i32;
167 let ci_idx = state.current_ci_idx();
168 if state.get_ci(ci_idx).top.0 < needed_top.0 {
169 let live_top = state.top_idx();
170 state.get_ci_mut(ci_idx).top = needed_top;
171 state.clear_stack_range(live_top, needed_top);
172 }
173 }
174 res
175}
176
177pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
194 if n <= 0 {
195 return;
196 }
197 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
198 return;
199 }
200 let abs_top = from.top_idx().0 as i32;
201 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
202 let first_abs = abs_top - n;
203 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
204 for i in 0..n {
205 let idx = StackIdx((first_abs + i) as u32);
206 buf.push(from.get_at(idx));
207 }
208 from.set_top(StackIdx(first_abs as u32));
209 for v in buf {
210 to.push(v);
211 }
212}
213
214pub fn at_panic(
215 state: &mut LuaState,
216 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
217) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
218 let old = state.global_mut().panic;
219 state.global_mut().panic = panicf;
220 old
221}
222
223pub fn version(_state: &LuaState) -> f64 {
224 504.0
225}
226
227pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
228 if idx > 0 || is_pseudo(idx) {
230 idx
231 } else {
232 let ci = state.current_call_info();
233 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
234 }
235}
236
237pub fn get_top(state: &LuaState) -> i32 {
238 let ci = state.current_call_info();
239 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
240}
241
242pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
243 let func = state.current_call_info().func;
244 let ci_top = state.current_call_info().top;
245 if idx >= 0 {
246 debug_assert!(
247 idx as u32 <= ci_top.saturating_sub(func + 1),
248 "new top too large"
249 );
250 let new_top = func + 1 + idx as i32;
251 let old_top = state.top_idx();
252 if new_top.0 > old_top.0 {
253 for i in old_top.0..new_top.0 {
254 state.set_at(i, LuaValue::Nil);
255 }
256 }
257 state.set_top_idx(new_top);
260 } else {
261 debug_assert!(
262 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
263 "invalid new top"
264 );
265 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
266 state.set_top_idx(new_top);
268 }
269 Ok(())
270}
271
272pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
273 let level = index_to_stack_idx(state, idx);
274 state.set_at(level, LuaValue::Nil);
276 Ok(())
277}
278
279#[inline]
280fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
281 let mut lo = from.0;
282 let mut hi = to.0;
283 while lo < hi {
284 let temp = state.get_at(StackIdx(lo));
285 let hi_val = state.get_at(StackIdx(hi));
286 state.set_at(StackIdx(lo), hi_val);
287 state.set_at(StackIdx(hi), temp);
288 lo += 1;
289 hi -= 1;
290 }
291}
292
293pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
294 let t = state.top_idx() - 1;
295 let p = index_to_stack_idx(state, idx);
296 debug_assert!(
297 (n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1),
298 "invalid 'n'"
299 );
300 let m = if n >= 0 {
301 t - n
302 } else {
303 StackIdx((p.0 as i32 - n - 1) as u32)
304 };
305 reverse_segment(state, p, m);
306 reverse_segment(state, m + 1, t);
307 reverse_segment(state, p, t);
308}
309
310pub fn copy(state: &mut LuaState, fromidx: i32, toidx: i32) {
311 let fr = index_to_value(state, fromidx);
312 if is_upvalue(toidx) {
313 let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
315 let func_val = state.get_at(state.current_call_info().func);
316 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
317 let wrote = {
318 let mut upvalues = ccl.upvalues.borrow_mut();
319 if upval_n >= 1 && upval_n <= upvalues.len() {
320 upvalues[upval_n - 1] = fr.clone();
321 true
322 } else {
323 false
324 }
325 };
326 if wrote {
327 state.gc().barrier(ccl, &fr);
328 }
329 }
330 } else if toidx == LUA_REGISTRYINDEX {
331 } else {
333 let to_slot = index_to_stack_idx(state, toidx);
334 state.set_at(to_slot, fr);
335 }
336}
337
338pub fn push_value(state: &mut LuaState, idx: i32) {
339 let v = index_to_value(state, idx);
340 state.push(v);
341}
342
343impl LuaState {
348 pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
349 push_value(self, idx);
350 Ok(())
351 }
352
353 pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
354 push_value(self, idx);
355 Ok(())
356 }
357
358 pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
359 rotate(self, idx, 1);
360 Ok(())
361 }
362
363 pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
369 len(self, idx)?;
370 let l = match to_integer_x(self, -1) {
371 Some(n) => n,
372 None => {
373 return Err(LuaError::runtime(format_args!(
374 "object length is not an integer"
375 )));
376 }
377 };
378 self.pop_n(1);
379 Ok(l)
380 }
381
382 pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
389 if let Some(write_fn) = self.global().stdout_hook {
390 write_fn(msg).map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
391 return Ok(());
392 }
393
394 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
395 {
396 let _ = msg;
397 Err(LuaError::runtime(format_args!(
398 "stdout not available in this host"
399 )))
400 }
401
402 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
403 {
404 use std::io::Write;
405 let stdout = std::io::stdout();
406 let mut handle = stdout.lock();
407 handle
408 .write_all(msg)
409 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
410 handle
411 .flush()
412 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
413 Ok(())
414 }
415 }
416
417 pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
426 let abs = abs_index(self, idx);
427 let v = index_to_value(self, abs);
428 let mt: Option<GcRef<LuaTable>> = match &v {
429 LuaValue::Table(t) => t.metatable(),
430 LuaValue::UserData(u) => u.metatable(),
431 _ => self.global().mt[v.base_type() as usize].clone(),
432 };
433 if let Some(mt_ref) = mt {
434 let key = self.intern_str(b"__tostring")?;
435 let f = mt_ref.get_short_str(&key);
436 if !matches!(f, LuaValue::Nil) {
437 let func_idx = self.top_idx();
438 self.push(f);
439 self.push(v.clone());
440 if self.current_ci().is_lua_code() {
441 self.do_call(func_idx, 1)?;
442 } else {
443 self.do_call_no_yield(func_idx, 1)?;
444 }
445 let top = self.top_idx();
446 let result = self.get_at(StackIdx(top.0 - 1));
447 if let LuaValue::Str(s) = result {
448 return Ok(s.as_bytes().to_vec());
449 }
450 return Err(LuaError::runtime(format_args!(
451 "'__tostring' must return a string"
452 )));
453 }
454 }
455 let bytes: Vec<u8> = match &v {
456 LuaValue::Str(s) => {
457 let out = s.as_bytes().to_vec();
458 self.push(LuaValue::Str(s.clone()));
459 out
460 }
461 LuaValue::Int(_) | LuaValue::Float(_) => {
462 let s = crate::object::num_to_string(self, &v)?;
463 let out = s.as_bytes().to_vec();
464 self.push(LuaValue::Str(s));
465 out
466 }
467 LuaValue::Bool(b) => {
468 let lit: &[u8] = if *b { b"true" } else { b"false" };
469 let s = self.intern_str(lit)?;
470 self.push(LuaValue::Str(s));
471 lit.to_vec()
472 }
473 LuaValue::Nil => {
474 let s = self.intern_str(b"nil")?;
475 self.push(LuaValue::Str(s));
476 b"nil".to_vec()
477 }
478 _ => {
479 let kind = crate::tagmethods::obj_type_name(self, &v)?;
480 let ptr = to_pointer(self, abs).unwrap_or(0);
481 let mut buf = kind;
482 buf.extend_from_slice(b": 0x");
483 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
484 let s = self.intern_str(&buf)?;
485 self.push(LuaValue::Str(s));
486 buf
487 }
488 };
489 Ok(bytes)
490 }
491
492 pub fn top(&mut self) -> i32 {
499 get_top(self)
500 }
501
502 pub fn get_top(&mut self) -> i32 {
506 get_top(self)
507 }
508
509 pub fn type_at(&mut self, idx: i32) -> LuaType {
514 lua_type_at(self, idx)
515 }
516
517 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
523 if lua_type_at(self, arg) == LuaType::None {
524 return Err(LuaError::arg_error(arg, "value expected"));
525 }
526 Ok(())
527 }
528
529 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
539 match to_lua_string(self, arg)? {
540 Some(s) => Ok(s.as_bytes().to_vec()),
541 None => {
542 let got = index_to_value(self, arg);
543 let got_name = if lua_type_at(self, arg) == LuaType::None {
544 b"no value".to_vec()
545 } else {
546 crate::tagmethods::obj_type_name(self, &got)?
547 };
548 let extramsg = format!(
549 "string expected, got {}",
550 String::from_utf8_lossy(&got_name)
551 );
552 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
553 }
554 }
555 }
556
557 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
569 match to_integer_x(self, arg) {
570 Some(d) => Ok(d),
571 None => {
572 if is_number(self, arg) {
573 Err(crate::debug::arg_error_impl(
574 self,
575 arg,
576 b"number has no integer representation",
577 ))
578 } else {
579 let got = index_to_value(self, arg);
580 let got_name = if lua_type_at(self, arg) == LuaType::None {
581 b"no value".to_vec()
582 } else {
583 crate::tagmethods::obj_type_name(self, &got)?
584 };
585 let extramsg = format!(
586 "number expected, got {}",
587 String::from_utf8_lossy(&got_name)
588 );
589 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
590 }
591 }
592 }
593 }
594
595 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
605 match to_number_x(self, arg) {
606 Some(d) => Ok(d),
607 None => {
608 let got = index_to_value(self, arg);
609 let got_name = if lua_type_at(self, arg) == LuaType::None {
610 b"no value".to_vec()
611 } else {
612 crate::tagmethods::obj_type_name(self, &got)?
613 };
614 let extramsg = format!(
615 "number expected, got {}",
616 String::from_utf8_lossy(&got_name)
617 );
618 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
619 }
620 }
621 }
622
623 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
633 match lua_type_at(self, arg) {
634 LuaType::None | LuaType::Nil => Ok(def),
635 _ => match to_integer_x(self, arg) {
636 Some(d) => Ok(d),
637 None => {
638 if is_number(self, arg) {
639 Err(LuaError::arg_error(
640 arg,
641 "number has no integer representation",
642 ))
643 } else {
644 let got = index_to_value(self, arg);
645 Err(LuaError::type_arg_error(arg, "number", &got))
646 }
647 }
648 },
649 }
650 }
651
652 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
659 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
660 }
661
662 pub fn protected_call_k(
666 &mut self,
667 nargs: i32,
668 nresults: i32,
669 msgh: i32,
670 ctx: isize,
671 k: Option<crate::state::LuaKFunction>,
672 ) -> Result<(), LuaError> {
673 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
674 }
675
676 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
677 push_lstring(self, s)?;
678 Ok(())
679 }
680
681 pub fn push_c_closure(
682 &mut self,
683 f: fn(&mut LuaState) -> Result<usize, LuaError>,
684 n: i32,
685 ) -> Result<(), LuaError> {
686 push_cclosure(self, f, n)
687 }
688
689 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
690 raw_set_i(self, idx, n)
691 }
692
693 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
694 set_i(self, idx, n)
695 }
696
697 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
701 get_i_value(self, t, n)
702 }
703
704 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
708 set_i_value(self, t, n)
709 }
710
711 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
712 create_table(self, narr, nrec)
713 }
714
715 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
719 set_field(self, LUA_REGISTRYINDEX, key)
720 }
721
722 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
728 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
729 return Ok(false);
730 }
731 self.pop_n(1);
732 create_table(self, 0, 2)?;
733 push_lstring(self, tname)?;
734 set_field(self, -2, b"__name")?;
735 push_value(self, -1);
736 set_field(self, LUA_REGISTRYINDEX, tname)?;
737 Ok(true)
738 }
739
740 pub fn new_lib(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
747 create_table(self, 0, funcs.len() as i32)?;
748 for (name, f) in funcs {
749 push_cclosure(self, *f, 0)?;
750 set_field(self, -2, name)?;
751 }
752 Ok(())
753 }
754
755 pub fn register_lib(
761 &mut self,
762 _name: &[u8],
763 funcs: &[(&[u8], LuaCFunction)],
764 ) -> Result<(), LuaError> {
765 self.new_lib(funcs)
766 }
767
768 pub fn new_lib_table(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
777 create_table(self, 0, funcs.len() as i32)
778 }
779
780 pub fn set_funcs_with_upvalues(
785 &mut self,
786 funcs: &[(&[u8], LuaCFunction)],
787 nup: i32,
788 ) -> Result<(), LuaError> {
789 check_stack(self, nup);
790 for (name, f) in funcs {
791 for _ in 0..nup {
792 push_value(self, -nup);
793 }
794 push_cclosure(self, *f, nup)?;
795 set_field(self, -(nup + 2), name)?;
796 }
797 self.pop_n(nup as usize);
798 Ok(())
799 }
800
801 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
802 set_metatable(self, objindex)?;
803 Ok(())
804 }
805
806 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
812 get_field(self, LUA_REGISTRYINDEX, name)?;
813 set_metatable(self, -2)?;
814 Ok(())
815 }
816
817 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
821 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
822 return Ok(true);
823 }
824 self.pop_n(1);
825 let idx = abs_index(self, LUA_REGISTRYINDEX);
826 let new_tbl = self.new_table();
827 self.push(LuaValue::Table(new_tbl));
828 push_value(self, -1);
829 set_field(self, idx, name)?;
830 Ok(false)
831 }
832
833 pub fn new_userdata_typed(
843 &mut self,
844 _name: &[u8],
845 size: usize,
846 nuvalue: i32,
847 ) -> Result<GcRef<LuaUserData>, LuaError> {
848 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
849 let u = GcRef::new(LuaUserData {
851 data: vec![0u8; size].into_boxed_slice(),
852 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
853 metatable: std::cell::RefCell::new(None),
854 host_value: std::cell::RefCell::new(None),
855 });
856 u.account_buffer(u.buffer_bytes() as isize);
857 self.push(LuaValue::UserData(u.clone()));
858 self.gc_pre_collect_clear();
859 self.gc().check_step();
860 Ok(u)
861 }
862}
863
864pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
867 if !is_valid_index(state, idx) {
868 return LuaType::None;
869 }
870 index_to_value(state, idx).base_type()
871}
872
873pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
874 t.type_name()
875}
876
877pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
878 let o = index_to_value(state, idx);
879 matches!(
880 o,
881 LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_))
882 )
883}
884
885pub fn is_integer(state: &LuaState, idx: i32) -> bool {
886 let o = index_to_value(state, idx);
887 matches!(o, LuaValue::Int(_))
888}
889
890pub fn is_number(state: &LuaState, idx: i32) -> bool {
891 let o = index_to_value(state, idx);
892 o.to_number_with_strconv().is_some()
893}
894
895pub fn is_string(state: &LuaState, idx: i32) -> bool {
896 let o = index_to_value(state, idx);
897 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
898}
899
900pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
901 let o = index_to_value(state, idx);
902 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
903}
904
905pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
906 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
907 return false;
908 }
909 let o1 = index_to_value(state, index1);
910 let o2 = index_to_value(state, index2);
911 state.equal_obj(None, &o1, &o2)
912}
913
914pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
916 const LUA_OPUNM: i32 = 12;
919 const LUA_OPBNOT: i32 = 14;
920 if op == LUA_OPUNM || op == LUA_OPBNOT {
921 let top_val = state.get_at(state.top_idx() - 1);
923 state.push(top_val);
924 }
925 let top = state.top_idx();
926 let a = state.get_at(top - 2);
927 let b = state.get_at(top - 1);
928 let result = state.arith_op(op, &a, &b)?;
929 state.set_at(top - 2, result);
930 state.pop();
931 Ok(())
932}
933
934pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
935 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
936 let o1 = index_to_value(state, index1);
937 let o2 = index_to_value(state, index2);
938 if valid {
939 match op {
940 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
941 1 => state.less_than(&o1, &o2),
942 2 => state.less_equal(&o1, &o2),
943 _ => {
944 debug_assert!(false, "invalid option");
945 Ok(false)
946 }
947 }
948 } else {
949 Ok(false)
950 }
951}
952
953pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
954 match state.str_to_num(s) {
956 Some((val, consumed)) => {
957 state.push(val);
958 consumed
959 }
960 None => 0,
961 }
962}
963
964pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
965 let o = index_to_value(state, idx);
966 o.to_number_with_strconv()
967}
968
969pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
970 let o = index_to_value(state, idx);
971 o.to_integer_with_strconv()
972}
973
974pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
975 let o = index_to_value(state, idx);
976 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
977}
978
979pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Option<GcRef<LuaString>>, LuaError> {
981 let o = index_to_value(state, idx);
982 if let LuaValue::Str(s) = &o {
983 return Ok(Some(s.clone()));
984 }
985 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
986 return Ok(None);
987 }
988 state.obj_to_string(idx)?;
989 state.gc_pre_collect_clear();
990 state.gc().check_step();
991 let updated = index_to_value(state, idx);
992 if let LuaValue::Str(s) = updated {
993 Ok(Some(s))
994 } else {
995 Ok(None)
996 }
997}
998
999pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
1000 let o = index_to_value(state, idx);
1001 match &o {
1002 LuaValue::Str(s) => s.len() as u64,
1003 LuaValue::UserData(u) => u.len() as u64,
1004 LuaValue::Table(t) => state.table_getn(t) as u64,
1005 _ => 0,
1006 }
1007}
1008
1009pub fn to_cfunction(
1010 state: &LuaState,
1011 idx: i32,
1012) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
1013 let o = index_to_value(state, idx);
1014 match o {
1015 LuaValue::Function(LuaClosure::LightC(_f)) => None,
1019 LuaValue::Function(LuaClosure::C(_ccl)) => None,
1020 _ => None,
1021 }
1022}
1023
1024#[inline]
1025fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1026 match o {
1027 LuaValue::UserData(u) => {
1028 let _ = u;
1032 None
1033 }
1034 LuaValue::LightUserData(p) => Some(*p),
1035 _ => None,
1036 }
1037}
1038
1039pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1040 let o = index_to_value(state, idx);
1041 to_userdata_ptr(&o)
1042}
1043
1044pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1045 let o = index_to_value(state, idx);
1049 if let LuaValue::Thread(t) = o {
1050 Some(t)
1051 } else {
1052 None
1053 }
1054}
1055
1056pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1059 let o = index_to_value(state, idx);
1060 match &o {
1063 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1064 LuaValue::LightUserData(p) => Some(*p as usize),
1065 LuaValue::Str(s) => Some(GcRef::identity(s)),
1066 LuaValue::Table(t) => Some(GcRef::identity(t)),
1067 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1068 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1069 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1070 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1071 _ => None,
1072 }
1073}
1074
1075pub fn push_nil(state: &mut LuaState) {
1078 state.push(LuaValue::Nil);
1079}
1080
1081pub fn push_number(state: &mut LuaState, n: f64) {
1082 state.push(LuaValue::Float(n));
1083}
1084
1085pub fn push_integer(state: &mut LuaState, n: i64) {
1086 state.push(LuaValue::Int(n));
1087}
1088
1089pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1091 let ts = state.intern_str(s)?;
1092 state.push(LuaValue::Str(ts.clone()));
1093 state.gc_pre_collect_clear();
1094 state.gc().check_step();
1095 Ok(ts)
1096}
1097
1098pub fn push_string(
1099 state: &mut LuaState,
1100 s: Option<&[u8]>,
1101) -> Result<Option<GcRef<LuaString>>, LuaError> {
1102 match s {
1103 None => {
1104 state.push(LuaValue::Nil);
1105 state.gc_pre_collect_clear();
1106 state.gc().check_step();
1107 Ok(None)
1108 }
1109 Some(bytes) => {
1110 let ts = state.intern_str(bytes)?;
1111 state.push(LuaValue::Str(ts.clone()));
1112 state.gc_pre_collect_clear();
1113 state.gc().check_step();
1114 Ok(Some(ts))
1115 }
1116 }
1117}
1118
1119pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1123 let ts = state.intern_str(formatted)?;
1124 state.push(LuaValue::Str(ts.clone()));
1125 state.gc_pre_collect_clear();
1126 state.gc().check_step();
1127 Ok(ts)
1128}
1129
1130pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1132 push_vfstring(state, formatted)
1133}
1134
1135pub fn push_cclosure(
1136 state: &mut LuaState,
1137 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1138 n: i32,
1139) -> Result<(), LuaError> {
1140 let idx: lua_types::closure::LuaCFnPtr = {
1154 let mut g = state.global_mut();
1155 if n == 0 {
1156 match g.c_functions.iter().position(|existing| {
1157 existing
1158 .as_bare()
1159 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1160 }) {
1161 Some(i) => i,
1162 None => {
1163 let i = g.c_functions.len();
1164 g.c_functions.push(LuaCallable::bare(f));
1165 i
1166 }
1167 }
1168 } else {
1169 let i = g.c_functions.len();
1170 g.c_functions.push(LuaCallable::bare(f));
1171 i
1172 }
1173 };
1174 if n == 0 {
1175 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1176 } else {
1177 debug_assert!(
1178 n > 0 && (n as u32) <= MAX_UPVAL as u32,
1179 "upvalue index too large"
1180 );
1181 let n_usize = n as usize;
1182 let top = state.top_idx();
1183 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1184 let base = top.0 as usize - n_usize;
1185 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1186 for i in 0..n_usize {
1187 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1188 }
1189 state.pop_n(n_usize);
1190 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1192 func: idx,
1193 upvalues: std::cell::RefCell::new(upvalues),
1194 }));
1195 if let LuaClosure::C(ccl) = &cl {
1196 ccl.account_buffer(ccl.buffer_bytes() as isize);
1197 }
1198 state.push(LuaValue::Function(cl));
1199 state.gc_pre_collect_clear();
1200 state.gc().check_step();
1201 }
1202 Ok(())
1203}
1204
1205pub fn push_boolean(state: &mut LuaState, b: bool) {
1206 state.push(LuaValue::Bool(b));
1207}
1208
1209pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1210 state.push(LuaValue::LightUserData(p));
1211}
1212
1213pub fn push_thread(state: &mut LuaState) -> bool {
1215 let (value, is_main) = {
1216 let g = state.global();
1217 let id = g.current_thread_id;
1218 let v = g
1219 .thread_value_for(id)
1220 .expect("current_thread_id must always resolve to a registered thread");
1221 (v, id == g.main_thread_id)
1222 };
1223 state.push(LuaValue::Thread(value));
1224 is_main
1225}
1226
1227fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1230 let str_val = {
1231 let ts = state.intern_str(k)?;
1232 LuaValue::Str(ts)
1233 };
1234 let result = state.table_get_with_tm(&t, &str_val)?;
1237 state.push(result);
1238 let top = state.top_idx();
1239 Ok(state.get_at(top - 1).base_type())
1240}
1241
1242fn get_global_table(state: &LuaState) -> LuaValue {
1243 state.global().globals.clone()
1249}
1250
1251pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1252 let g = get_global_table(state);
1253 aux_get_str(state, g, name)
1254}
1255
1256pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1257 let t = index_to_value(state, idx);
1258 let top = state.top_idx();
1259 let key = state.get_at(top - 1);
1260 let result = state.table_get_with_tm(&t, &key)?;
1261 state.set_at(top - 1, result);
1262 let val = state.get_at(top - 1);
1263 Ok(val.base_type())
1264}
1265
1266pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1267 let t = index_to_value(state, idx);
1268 aux_get_str(state, t, k)
1269}
1270
1271pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1272 let t = index_to_value(state, idx);
1273 let key = LuaValue::Int(n);
1274 let result = state.table_get_with_tm(&t, &key)?;
1275 state.push(result);
1276 let top = state.top_idx();
1277 Ok(state.get_at(top - 1).base_type())
1278}
1279
1280pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1286 let key = LuaValue::Int(n);
1287 let result = state.table_get_with_tm(t, &key)?;
1288 state.push(result);
1289 let top = state.top_idx();
1290 Ok(state.get_at(top - 1).base_type())
1291}
1292
1293fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1294 let v = val.unwrap_or(LuaValue::Nil);
1295 state.push(v);
1296 let top = state.top_idx();
1297 state.get_at(top - 1).base_type()
1298}
1299
1300fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1301 let t = index_to_value(state, idx);
1302 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1303 if let LuaValue::Table(tbl) = t {
1304 Some(tbl)
1305 } else {
1306 None
1307 }
1308}
1309
1310pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1311 let t = get_table_value(state, idx);
1312 let top = state.top_idx();
1313 let key = state.get_at(top - 1);
1314 let val = t.as_ref().map(|tbl| tbl.get(&key));
1315 state.set_top_idx(top - 1);
1316 finish_raw_get(state, val)
1317}
1318
1319pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1320 let t = get_table_value(state, idx);
1321 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1322 finish_raw_get(state, val)
1323}
1324
1325pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1326 let t = get_table_value(state, idx);
1327 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1328 let val = t.as_ref().map(|tbl| tbl.get(&key));
1329 finish_raw_get(state, val)
1330}
1331
1332pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1333 let t = state.new_table();
1334 if narray > 0 || nrec > 0 {
1335 t.resize(state, narray as usize, nrec as usize)?;
1336 }
1337 state.push(LuaValue::Table(t));
1338 state.gc_pre_collect_clear();
1339 state.gc().check_step();
1340 Ok(())
1341}
1342
1343pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1344 let obj = index_to_value(state, objindex);
1345 let mt: Option<GcRef<LuaTable>> = match &obj {
1346 LuaValue::Table(t) => t.metatable(),
1347 LuaValue::UserData(u) => u.metatable(),
1348 other => {
1349 let idx = other.base_type() as usize;
1350 state.global().mt[idx].clone()
1351 }
1352 };
1353 if let Some(mt_table) = mt {
1354 state.push(LuaValue::Table(mt_table));
1355 true
1356 } else {
1357 false
1358 }
1359}
1360
1361pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1362 let o = index_to_value(state, idx);
1363 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1364 if let LuaValue::UserData(ref u) = o {
1365 let uv = u.uv.borrow();
1366 let uv_count = uv.len() as i32;
1367 if n <= 0 || n > uv_count {
1368 state.push(LuaValue::Nil);
1369 LuaType::None
1370 } else {
1371 let val = uv[(n - 1) as usize].clone();
1372 let t = val.base_type();
1373 state.push(val);
1374 t
1375 }
1376 } else {
1377 state.push(LuaValue::Nil);
1378 LuaType::None
1379 }
1380}
1381
1382fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1385 let str_val = {
1386 let ts = state.intern_str(k)?;
1387 LuaValue::Str(ts)
1388 };
1389 let top = state.top_idx();
1394 let val = state.get_at(top - 1);
1395 state.table_set_with_tm(&t, str_val, val)?;
1396 state.pop();
1397 Ok(())
1398}
1399
1400pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1401 let g = get_global_table(state);
1402 aux_set_str(state, g, name)
1403}
1404
1405pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1406 let t = index_to_value(state, idx);
1407 let top = state.top_idx();
1408 let key = state.get_at(top - 2);
1409 let val = state.get_at(top - 1);
1410 state.table_set_with_tm(&t, key, val)?;
1411 state.set_top_idx(top - 2);
1412 Ok(())
1413}
1414
1415pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1416 let t = index_to_value(state, idx);
1417 aux_set_str(state, t, k)
1418}
1419
1420pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1421 let t = index_to_value(state, idx);
1422 let top = state.top_idx();
1423 let val = state.get_at(top - 1);
1424 let key = LuaValue::Int(n);
1425 state.table_set_with_tm(&t, key, val)?;
1426 state.pop();
1427 Ok(())
1428}
1429
1430pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1436 let top = state.top_idx();
1437 let val = state.get_at(top - 1);
1438 let key = LuaValue::Int(n);
1439 state.table_set_with_tm(t, key, val)?;
1440 state.pop();
1441 Ok(())
1442}
1443
1444fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1445 let t = get_table_value(state, idx)
1446 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1447 let top = state.top_idx();
1448 let val = state.get_at(top - 1);
1449 t.raw_set(state, key, val)?;
1450 t.invalidate_tm_cache();
1451 let top_val = state.get_at(top - 1);
1452 state.gc().table_barrier_back(&t, &top_val);
1453 state.set_top_idx(top - n as i32);
1454 Ok(())
1455}
1456
1457pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1458 let top = state.top_idx();
1459 let key = state.get_at(top - 2);
1460 aux_raw_set(state, idx, key, 2)
1461}
1462
1463pub fn raw_set_p(
1464 state: &mut LuaState,
1465 idx: i32,
1466 p: *const core::ffi::c_void,
1467) -> Result<(), LuaError> {
1468 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1469 aux_raw_set(state, idx, key, 1)
1470}
1471
1472pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1473 let t = get_table_value(state, idx)
1474 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1475 let top = state.top_idx();
1476 let val = state.get_at(top - 1);
1477 t.raw_set_int(state, n, val)?;
1478 let top_val = state.get_at(top - 1);
1479 state.gc().table_barrier_back(&t, &top_val);
1480 state.pop();
1481 Ok(())
1482}
1483
1484fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1489 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1490 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1491}
1492
1493fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1494 let heap_ptr = object.heap_ptr();
1495 let mut g = state.global_mut();
1496 if g.finalizers.push_pending_unique(object) {
1497 if let Some(ptr) = heap_ptr {
1498 g.heap.move_allgc_to_finobj(ptr);
1499 }
1500 }
1501}
1502
1503pub fn run_pending_finalizers(state: &mut LuaState) {
1506 let _ = run_pending_finalizers_inner(state, false);
1507}
1508
1509const GC_FIN_MAX: usize = 10;
1510
1511pub fn run_pending_finalizers_inner(state: &mut LuaState, propagate: bool) -> Result<(), LuaError> {
1531 run_pending_finalizers_limited(state, propagate, None).map(|_| ())
1532}
1533
1534pub(crate) fn run_some_pending_finalizers_inner(
1535 state: &mut LuaState,
1536 propagate: bool,
1537) -> Result<usize, LuaError> {
1538 run_pending_finalizers_limited(state, propagate, Some(GC_FIN_MAX))
1539}
1540
1541fn run_pending_finalizers_limited(
1542 state: &mut LuaState,
1543 propagate: bool,
1544 limit: Option<usize>,
1545) -> Result<usize, LuaError> {
1546 let version = state.global().lua_version;
1547 let mut processed = 0usize;
1548 loop {
1549 if limit.map_or(false, |limit| processed >= limit) {
1550 break;
1551 }
1552 if !state.global().finalizers.has_to_be_finalized() {
1556 break;
1557 }
1558 let object = {
1568 let mut g = state.global_mut();
1569 let object = g
1570 .finalizers
1571 .pop_to_be_finalized()
1572 .expect("to-be-finalized checked non-empty");
1573 if let Some(ptr) = object.heap_ptr() {
1574 g.heap.move_tobefnz_to_allgc(ptr);
1575 }
1576 object
1577 };
1578 processed += 1;
1579 let mt = object.metatable();
1580 let gc_fn = match mt {
1581 Some(ref m) => {
1582 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1583 m.get_short_str(&name)
1584 }
1585 None => LuaValue::Nil,
1586 };
1587 if !matches!(gc_fn, LuaValue::Function(_)) {
1588 continue;
1589 }
1590 let saved_top = state.top_idx();
1591 let ci_top = state.current_call_info().top;
1592 if saved_top.0 < ci_top.0 {
1593 state.clear_stack_range(saved_top, ci_top);
1594 state.set_top(ci_top);
1595 }
1596 state.push(gc_fn);
1597 state.push(object.as_lua_value());
1598 let func_idx = state.top_idx() - 2;
1599 let _heap_guard = {
1600 let g = state.global.borrow();
1601 lua_gc::HeapGuard::push(&g.heap)
1602 };
1603 let old_allowhook = state.allowhook;
1604 let old_gcstp = state.global_mut().stop_gc_internal();
1605 state.allowhook = false;
1606 let caller_ci = state.ci;
1607 let caller_status = state.get_ci(caller_ci).callstatus;
1608 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1609 let status = crate::do_::pcall(state, |s| s.call_no_yield(func_idx, 0), func_idx, 0);
1610 let finalizer_error: Option<LuaValue> = if status != LuaStatus::Ok {
1614 Some(state.get_at(state.top_idx() - 1).clone())
1615 } else {
1616 None
1617 };
1618 state.get_ci_mut(caller_ci).callstatus = caller_status;
1619 state.allowhook = old_allowhook;
1620 state.global_mut().set_gc_stop_flags(old_gcstp);
1621 state.set_top(saved_top);
1622
1623 if let Some(errobj) = finalizer_error {
1624 match version {
1625 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1626 let msg: Vec<u8> = match &errobj {
1633 LuaValue::Str(s) => s.as_bytes().to_vec(),
1634 _ => b"no message".to_vec(),
1635 };
1636 let mut wrapped = b"error in __gc metamethod (".to_vec();
1637 wrapped.extend_from_slice(&msg);
1638 wrapped.push(b')');
1639 let interned = state.intern_str(&wrapped)?;
1640 return Err(LuaError::from_value(LuaValue::Str(interned)));
1641 }
1642 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1643 let msg: Vec<u8> = match &errobj {
1647 LuaValue::Str(s) => s.as_bytes().to_vec(),
1648 _ => b"error object is not a string".to_vec(),
1649 };
1650 state.emit_warning(b"error in ", true);
1651 state.emit_warning(b"__gc", true);
1652 state.emit_warning(b" (", true);
1653 state.emit_warning(&msg, true);
1654 state.emit_warning(b")", false);
1655 }
1656 _ => {}
1657 }
1658 }
1659 }
1660 Ok(processed)
1664}
1665
1666pub fn run_close_finalizers(state: &mut LuaState) {
1685 let pending: Vec<FinalizerObject> = state.global_mut().finalizers.take_pending();
1686 if pending.is_empty() {
1687 return;
1688 }
1689 let mut seen = std::collections::HashSet::<usize>::new();
1690 {
1691 let mut g = state.global_mut();
1692 for object in pending.into_iter().rev() {
1693 let heap_ptr = object.heap_ptr();
1694 if seen.insert(object.identity()) {
1695 g.finalizers.push_to_be_finalized(object);
1696 if let Some(ptr) = heap_ptr {
1697 g.heap.move_finobj_to_tobefnz(ptr);
1698 }
1699 }
1700 }
1701 }
1702 run_pending_finalizers(state);
1703}
1704
1705fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1711 let mut g = state.global_mut();
1712 g.weak_tables_registry.live_snapshot()
1713}
1714
1715pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1716 let top = state.top_idx();
1717 let mt_val = state.get_at(top - 1);
1718 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1719 None
1720 } else {
1721 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1722 if let LuaValue::Table(t) = mt_val {
1723 Some(t)
1724 } else {
1725 None
1726 }
1727 };
1728
1729 let obj = index_to_value(state, objindex);
1730 match obj {
1731 LuaValue::Table(ref tbl) => {
1732 if mt.is_some() {
1733 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1734 }
1735 tbl.set_metatable(mt.clone());
1736 if tbl.weak_mode() == 0 {
1737 state
1738 .global_mut()
1739 .weak_tables_registry
1740 .remove_identity(tbl.identity());
1741 } else {
1742 state
1743 .global_mut()
1744 .weak_tables_registry
1745 .push_unique(WeakTableEntry::new(tbl));
1746 }
1747 let tables_finalizable =
1757 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1758 if tables_finalizable {
1759 if let Some(ref mt_table) = mt {
1760 if metatable_has_gc(state, mt_table) {
1761 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1762 }
1763 }
1764 }
1765 }
1766 LuaValue::UserData(ref ud) => {
1767 if let Some(ref mt_table) = mt {
1768 state.gc().obj_barrier(ud, mt_table);
1769 if metatable_has_gc(state, mt_table) {
1770 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1771 }
1772 }
1773 ud.set_metatable(mt);
1774 }
1775 ref other => {
1776 let idx = other.base_type() as usize;
1777 state.global_mut().mt[idx] = mt;
1778 }
1779 }
1780 state.pop();
1781 Ok(true)
1782}
1783
1784pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1785 let o = index_to_value(state, idx);
1786 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1787 let top = state.top_idx();
1788 let val = state.get_at(top - 1);
1789 let res = if let LuaValue::UserData(ref ud) = o {
1790 let mut uv = ud.uv.borrow_mut();
1791 let nuvalue = uv.len() as i32;
1792 if n < 1 || n > nuvalue {
1793 false
1794 } else {
1795 uv[(n - 1) as usize] = val.clone();
1796 drop(uv);
1797 state.gc().barrier_back(ud, &val);
1798 true
1799 }
1800 } else {
1801 false
1802 };
1803 state.pop();
1804 Ok(res)
1805}
1806
1807pub fn call_k(
1811 state: &mut LuaState,
1812 nargs: i32,
1813 nresults: i32,
1814 ctx: isize,
1815 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1816) -> Result<(), LuaError> {
1817 let top = state.top_idx();
1818 let func_idx = top - (nargs + 1);
1819 if k.is_some() && state.is_yieldable() {
1825 let ci_idx = state.ci;
1826 {
1827 let ci = state.get_ci_mut(ci_idx);
1828 ci.set_u_c_k(k);
1829 ci.set_u_c_ctx(ctx);
1830 }
1831 state.call_at(func_idx, nresults)?;
1832 } else {
1833 state.call_no_yield(func_idx, nresults)?;
1834 }
1835 state.adjust_results(nresults);
1836 Ok(())
1837}
1838
1839pub fn pcall_k(
1841 state: &mut LuaState,
1842 nargs: i32,
1843 nresults: i32,
1844 errfunc: i32,
1845 ctx: isize,
1846 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1847) -> Result<LuaStatus, LuaError> {
1848 let _heap_guard = {
1853 let g = state.global.borrow();
1854 lua_gc::HeapGuard::push(&g.heap)
1859 };
1860 let err_handler_idx: isize = if errfunc == 0 {
1861 0
1862 } else {
1863 let o = index_to_stack_idx(state, errfunc);
1864 debug_assert!(
1865 matches!(state.get_at(o), LuaValue::Function(_)),
1866 "error handler must be a function"
1867 );
1868 o.0 as isize
1869 };
1870 let top = state.top_idx();
1871 let func_idx = top - (nargs + 1);
1872 if k.is_none() || !state.is_yieldable() {
1873 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
1874 state.adjust_results(nresults);
1875 return Ok(LuaStatus::Ok);
1876 }
1877 let ci_idx = state.ci;
1883 let allow = state.allowhook;
1884 let saved_errfunc = state.errfunc;
1885 {
1886 let ci = state.get_ci_mut(ci_idx);
1887 ci.set_u_c_k(k);
1888 ci.set_u_c_ctx(ctx);
1889 ci.set_u2_funcidx(func_idx.0 as i32);
1890 ci.set_u_c_old_errfunc(saved_errfunc);
1891 ci.set_oah(allow);
1892 ci.callstatus |= crate::state::CIST_YPCALL;
1893 }
1894 state.errfunc = err_handler_idx;
1895 let call_result = crate::do_::call(state, func_idx, nresults);
1896 match call_result {
1897 Ok(()) => {
1898 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
1901 state.errfunc = saved_errfunc;
1902 state.adjust_results(nresults);
1903 Ok(LuaStatus::Ok)
1904 }
1905 Err(crate::state::LuaError::Yield) => {
1906 Err(crate::state::LuaError::Yield)
1910 }
1911 Err(e) => {
1912 Err(e)
1916 }
1917 }
1918}
1919
1920pub fn load(
1924 state: &mut LuaState,
1925 reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
1926 chunkname: Option<&[u8]>,
1927 mode: Option<&[u8]>,
1928) -> Result<LuaStatus, LuaError> {
1929 let name = chunkname.unwrap_or(b"?");
1930 let z = crate::zio::ZIO::new(reader);
1931 let status = state.protected_parser(z, name, mode);
1932 if status == LuaStatus::Ok {
1933 let top = state.top_idx();
1934 let func_val = state.get_at(top - 1);
1935 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
1936 if !lcl.upvals.is_empty() {
1937 let gt = get_global_table(state);
1938 let uv = state.new_upval_closed(gt);
1939 lcl.set_upval(0, uv);
1940 state.gc().obj_barrier(&lcl, &uv);
1941 }
1942 }
1943 }
1944 Ok(status)
1945}
1946
1947pub fn dump(
1948 state: &LuaState,
1949 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
1950 strip: bool,
1951) -> Result<bool, LuaError> {
1952 let top = state.top_idx();
1953 let o = state.get_at(top - 1);
1954 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
1955 crate::dump::dump(state, &lcl.proto, writer, strip)?;
1956 Ok(true)
1957 } else {
1958 Ok(false)
1959 }
1960}
1961
1962pub fn status(state: &LuaState) -> LuaStatus {
1963 LuaStatus::from_raw(state.status as i32)
1964}
1965
1966#[repr(i32)]
1970#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1971pub enum GcWhat {
1972 Stop = 0,
1973 Restart = 1,
1974 Collect = 2,
1975 Count = 3,
1976 CountB = 4,
1977 Step = 5,
1978 SetPause = 6,
1979 SetStepMul = 7,
1980 IsRunning = 9,
1981 Gen = 10,
1982 Inc = 11,
1983}
1984
1985pub enum GcArgs {
1987 Stop,
1988 Restart,
1989 Collect,
1990 Count,
1991 CountB,
1992 Step {
1993 data: i32,
1994 },
1995 SetPause {
1996 value: i32,
1997 },
1998 SetStepMul {
1999 value: i32,
2000 },
2001 IsRunning,
2002 Gen {
2003 minormul: i32,
2004 majormul: i32,
2005 },
2006 Inc {
2007 pause: i32,
2008 stepmul: i32,
2009 stepsize: i32,
2010 },
2011 Param {
2014 param: usize,
2015 value: i64,
2016 },
2017}
2018
2019pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
2020 if let GcArgs::Param { param, value } = &args {
2024 return state.global_mut().gc55_param(*param, *value) as i32;
2025 }
2026 if state.global().is_gc_stopped_internally() {
2027 return -1;
2028 }
2029 match args {
2030 GcArgs::Stop => {
2031 state.global_mut().set_gc_stop_user();
2032 }
2033 GcArgs::Restart => {
2034 {
2035 let mut g = state.global_mut();
2036 crate::state::set_debt(&mut *g, 0);
2037 }
2038 state.global_mut().clear_gc_stop();
2039 }
2040 GcArgs::Collect => {
2041 if !state.allowhook {
2042 return 0;
2043 }
2044 state.gc().full_collect();
2050 if let Err(e) = run_pending_finalizers_inner(state, true) {
2058 state.global_mut().gc_finalizer_error = Some(e.into_value());
2059 }
2060 }
2061 GcArgs::Count => {
2062 let g = state.global();
2063 let total = g.heap.bytes_used();
2064 return (total >> 10) as i32;
2065 }
2066 GcArgs::CountB => {
2067 let g = state.global();
2068 let total = g.heap.bytes_used();
2069 return (total & 0x3ff) as i32;
2070 }
2071 GcArgs::Step { data } => {
2072 let old_stp = {
2073 let mut g = state.global_mut();
2074 let old = g.gc_stop_flags();
2075 g.clear_gc_stop();
2076 old
2077 };
2078 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2085 let work_units = if data == 0 {
2086 stepmul
2087 } else {
2088 let raw = (data as isize).saturating_mul(stepmul);
2089 raw.max(1)
2090 };
2091 let debt_for_result = if data == 0 {
2092 let mut g = state.global_mut();
2093 crate::state::set_debt(&mut *g, 0);
2094 0
2095 } else {
2096 let debt = data as isize * 1024 + state.global().gc_debt();
2097 let mut g = state.global_mut();
2098 crate::state::set_debt(&mut *g, debt);
2099 debt
2100 };
2101 let gen_mode = state.global().is_gen_mode();
2102 let cycle_complete = if gen_mode {
2103 if data == 0 {
2104 state.gc().generational_step_minor_only();
2105 } else {
2106 state.gc().generational_step();
2107 }
2108 if state.global().finalizers.has_to_be_finalized() {
2109 if let Err(e) = run_pending_finalizers_inner(state, true) {
2110 state.global_mut().gc_finalizer_error = Some(e.into_value());
2111 }
2112 }
2113 debt_for_result > 0 && state.global().gc_at_pause()
2114 } else if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2115 && state.global().finalizers.has_to_be_finalized()
2116 {
2117 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2118 state.global_mut().gc_finalizer_error = Some(e.into_value());
2119 }
2120 if state.global().finalizers.has_to_be_finalized() {
2121 false
2122 } else {
2123 state.global().heap.finish_callfin_phase()
2124 }
2125 } else {
2126 let completed = state.gc().incremental_step(work_units);
2127 if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2128 && state.global().finalizers.has_to_be_finalized()
2129 {
2130 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2131 state.global_mut().gc_finalizer_error = Some(e.into_value());
2132 }
2133 if state.global().finalizers.has_to_be_finalized() {
2134 false
2135 } else {
2136 state.global().heap.finish_callfin_phase()
2137 }
2138 } else {
2139 completed
2140 }
2141 };
2142 state.global_mut().set_gc_stop_flags(old_stp);
2143 {
2145 let heap_state = state.global().heap.gc_state();
2146 let mut g = state.global_mut();
2147 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2148 }
2149 return if cycle_complete { 1 } else { 0 };
2150 }
2151 GcArgs::SetPause { value } => {
2152 let old = state.global().gc_pause_param();
2153 state.global_mut().set_gc_pause_param(value);
2154 return old;
2155 }
2156 GcArgs::SetStepMul { value } => {
2157 let old = state.global().gc_stepmul_param();
2158 state.global_mut().set_gc_stepmul_param(value);
2159 return old;
2160 }
2161 GcArgs::IsRunning => {
2162 return state.global().gc_running() as i32;
2163 }
2164 GcArgs::Gen { minormul, majormul } => {
2165 let old_mode = if state.global().is_gen_mode() {
2166 10i32
2167 } else {
2168 11i32
2169 };
2170 if minormul != 0 {
2171 state.global_mut().genminormul = minormul as u8;
2172 }
2173 if majormul != 0 {
2174 state.global_mut().set_gc_genmajormul(majormul);
2175 }
2176 state.gc().change_mode(crate::state::GcKind::Generational);
2177 return old_mode;
2178 }
2179 GcArgs::Inc {
2180 pause,
2181 stepmul,
2182 stepsize,
2183 } => {
2184 let old_mode = if state.global().is_gen_mode() {
2185 10i32
2186 } else {
2187 11i32
2188 };
2189 if pause != 0 {
2190 state.global_mut().set_gc_pause_param(pause);
2191 }
2192 if stepmul != 0 {
2193 state.global_mut().set_gc_stepmul_param(stepmul);
2194 }
2195 if stepsize != 0 {
2196 state.global_mut().gcstepsize = stepsize as u8;
2197 }
2198 state.gc().change_mode(crate::state::GcKind::Incremental);
2199 return old_mode;
2200 }
2201 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2202 }
2203 0
2204}
2205
2206pub fn configure_startup_gc_mode(state: &mut LuaState) {
2213 if matches!(
2214 state.global().lua_version,
2215 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2216 ) {
2217 let _ = gc(state, GcArgs::Restart);
2218 let _ = gc(
2219 state,
2220 GcArgs::Gen {
2221 minormul: 0,
2222 majormul: 0,
2223 },
2224 );
2225 }
2226}
2227
2228pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2235 let top = state.top_idx();
2239 let errobj = state.get_at(top - 1);
2240 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2241 let memerr = state.global().memerrmsg.clone();
2242 GcRef::ptr_eq(s, &memerr)
2243 } else {
2244 false
2245 };
2246 if is_mem_err {
2247 Err(LuaError::Memory)
2248 } else {
2249 Err(LuaError::from_value(errobj))
2250 }
2251}
2252
2253pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2254 let t = get_table_value(state, idx)
2255 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2256 let top = state.top_idx();
2257 let key = state.get_at(top - 1);
2258 match t.next(key)? {
2259 Some((next_key, next_val)) => {
2260 state.set_at(top - 1, next_key);
2261 state.push(next_val);
2262 Ok(true)
2263 }
2264 None => {
2265 state.set_top_idx(top - 1);
2266 Ok(false)
2267 }
2268 }
2269}
2270
2271pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2272 let _level = index_to_stack_idx(state, idx);
2273 Ok(())
2276}
2277
2278pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2279 if n > 0 {
2280 state.concat(n)?;
2281 } else {
2282 let empty = state.intern_str(b"")?;
2283 state.push(LuaValue::Str(empty));
2284 }
2285 state.gc_pre_collect_clear();
2286 state.gc().check_step();
2287 Ok(())
2288}
2289
2290pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2291 let t = index_to_value(state, idx);
2292 let result = state.obj_len(&t)?;
2293 state.push(result);
2294 Ok(())
2295}
2296
2297pub fn set_warn_f(state: &mut LuaState, f: Option<Box<dyn FnMut(&[u8], bool)>>) {
2302 state.global_mut().warnf = f;
2304}
2305
2306pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2307 state.emit_warning(msg, tocont);
2308}
2309
2310pub fn new_userdata_uv(
2311 state: &mut LuaState,
2312 size: usize,
2313 nuvalue: i32,
2314) -> Result<GcRef<LuaUserData>, LuaError> {
2315 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2316 let u = state.new_userdata(size, nuvalue as usize)?;
2317 state.push(LuaValue::UserData(u.clone()));
2318 state.gc_pre_collect_clear();
2319 state.gc().check_step();
2320 Ok(u)
2321}
2322
2323fn aux_upvalue(state: &LuaState, fi: &LuaValue, n: i32) -> Option<(Vec<u8>, LuaValue)> {
2329 match fi {
2330 LuaValue::Function(LuaClosure::C(ccl)) => {
2331 let upvalues = ccl.upvalues.borrow();
2332 let nupvalues = upvalues.len() as i32;
2333 if n < 1 || n > nupvalues {
2334 return None;
2335 }
2336 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2337 }
2338 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2339 let nupvalues = lcl.upvals.len() as i32;
2340 if n < 1 || n > nupvalues {
2341 return None;
2342 }
2343 let val = state.upvalue_get(lcl, (n - 1) as usize);
2344 let name: Vec<u8> = lcl
2348 .proto
2349 .upvalues
2350 .get((n - 1) as usize)
2351 .and_then(|ud| ud.name.as_ref())
2352 .map(|s| s.as_bytes().to_vec())
2353 .unwrap_or_else(|| b"(no name)".to_vec());
2354 Some((name, val))
2355 }
2356 _ => None,
2357 }
2358}
2359
2360pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2361 let fi = index_to_value(state, funcindex);
2362 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2363 state.push(val);
2364 Some(name)
2365 } else {
2366 None
2367 }
2368}
2369
2370pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2371 let fi = index_to_value(state, funcindex);
2372 let (name, _) = aux_upvalue(state, &fi, n)?;
2373 let new_val = state.pop();
2374 match &fi {
2375 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2376 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2377 }
2378 LuaValue::Function(LuaClosure::C(ccl)) => {
2379 let idx = (n - 1) as usize;
2380 {
2381 let mut upvalues = ccl.upvalues.borrow_mut();
2382 if idx >= upvalues.len() {
2383 return None;
2384 }
2385 upvalues[idx] = new_val.clone();
2386 }
2387 state.gc().barrier(ccl, &new_val);
2388 }
2389 _ => return None,
2390 }
2391 Some(name)
2392}
2393
2394fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2397 let fi = index_to_value(state, fidx);
2398 debug_assert!(
2399 matches!(fi, LuaValue::Function(LuaClosure::Lua(_))),
2400 "Lua function expected"
2401 );
2402 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2403 let sizeupvalues = lcl.upvals.len() as i32;
2404 if n >= 1 && n <= sizeupvalues {
2405 Some((n - 1) as usize)
2406 } else {
2407 None
2408 }
2409 } else {
2410 None
2411 }
2412}
2413
2414pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2416 let fi = index_to_value(state, fidx);
2417 match &fi {
2418 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2419 let idx = get_upval_ref_idx(state, fidx, n)?;
2420 Some(GcRef::identity(&lcl.upval(idx)))
2422 }
2423 LuaValue::Function(LuaClosure::C(ccl)) => {
2424 let upvalues = ccl.upvalues.borrow();
2425 if n >= 1 && n <= upvalues.len() as i32 {
2426 Some(GcRef::identity(ccl) ^ (n as usize))
2429 } else {
2430 None
2431 }
2432 }
2433 LuaValue::Function(LuaClosure::LightC(_)) => None,
2434 _ => {
2435 debug_assert!(false, "function expected");
2436 None
2437 }
2438 }
2439}
2440
2441pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2443 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2444 Some(i) => i,
2445 None => return,
2446 };
2447 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2448 Some(i) => i,
2449 None => return,
2450 };
2451 let f1 = index_to_value(state, fidx1);
2452 let f2 = index_to_value(state, fidx2);
2453 if let (LuaValue::Function(LuaClosure::Lua(lcl1)), LuaValue::Function(LuaClosure::Lua(lcl2))) =
2454 (&f1, &f2)
2455 {
2456 let shared = lcl2.upval(idx2);
2457 lcl1.set_upval(idx1, shared);
2458 state.gc().obj_barrier(lcl1, &shared);
2459 }
2460}
2461
2462