1#![allow(dead_code)]
9
10#[allow(unused_imports)]
11use crate::prelude::*;
12use std::convert::Infallible;
13
14use crate::state::{
15 FinalizerObject, LuaCFunction, 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
32pub(crate) const 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) {
193 if n <= 0 {
194 return;
195 }
196 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
197 return;
198 }
199 let abs_top = from.top_idx().0 as i32;
200 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
201 let first_abs = abs_top - n;
202 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
203 for i in 0..n {
204 let idx = StackIdx((first_abs + i) as u32);
205 buf.push(from.get_at(idx));
206 }
207 from.set_top(StackIdx(first_abs as u32));
208 for v in buf {
209 to.push(v);
210 }
211}
212
213pub fn at_panic(
214 state: &mut LuaState,
215 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
216) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
217 let old = state.global_mut().panic;
218 state.global_mut().panic = panicf;
219 old
220}
221
222pub fn version(_state: &LuaState) -> f64 {
223 504.0
224}
225
226pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
227 if idx > 0 || is_pseudo(idx) {
229 idx
230 } else {
231 let ci = state.current_call_info();
232 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
233 }
234}
235
236pub fn get_top(state: &LuaState) -> i32 {
237 let ci = state.current_call_info();
238 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
239}
240
241pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
242 let func = state.current_call_info().func;
243 let ci_top = state.current_call_info().top;
244 if idx >= 0 {
245 debug_assert!(
246 idx as u32 <= ci_top.saturating_sub(func + 1),
247 "new top too large"
248 );
249 let new_top = func + 1 + idx as i32;
250 let old_top = state.top_idx();
251 if new_top.0 > old_top.0 {
252 for i in old_top.0..new_top.0 {
253 state.set_at(i, LuaValue::Nil);
254 }
255 }
256 state.set_top_idx(new_top);
257 } else {
258 debug_assert!(
259 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
260 "invalid new top"
261 );
262 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
263 state.set_top_idx(new_top);
264 }
265 Ok(())
266}
267
268pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
274 let level = index_to_stack_idx(state, idx);
275 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) {
315 let fr = index_to_value(state, fromidx);
316 if is_upvalue(toidx) {
317 let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
319 let func_val = state.get_at(state.current_call_info().func);
320 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
321 let wrote = {
322 let mut upvalues = ccl.upvalues.borrow_mut();
323 if upval_n >= 1 && upval_n <= upvalues.len() {
324 upvalues[upval_n - 1] = fr.clone();
325 true
326 } else {
327 false
328 }
329 };
330 if wrote {
331 state.gc().barrier(ccl, &fr);
332 }
333 }
334 } else if toidx == LUA_REGISTRYINDEX {
335 } else {
336 let to_slot = index_to_stack_idx(state, toidx);
337 state.set_at(to_slot, fr);
338 }
339}
340
341pub fn push_value(state: &mut LuaState, idx: i32) {
342 let v = index_to_value(state, idx);
343 state.push(v);
344}
345
346impl LuaState {
351 pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
352 push_value(self, idx);
353 Ok(())
354 }
355
356 pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
357 push_value(self, idx);
358 Ok(())
359 }
360
361 pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
362 rotate(self, idx, 1);
363 Ok(())
364 }
365
366 pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
372 len(self, idx)?;
373 let l = match to_integer_x(self, -1) {
374 Some(n) => n,
375 None => {
376 return Err(LuaError::runtime(format_args!(
377 "object length is not an integer"
378 )));
379 }
380 };
381 self.pop_n(1);
382 Ok(l)
383 }
384
385 pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
392 if let Some(write_fn) = self.global().stdout_hook {
393 write_fn(msg).map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
394 return Ok(());
395 }
396
397 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
398 {
399 let _ = msg;
400 Err(LuaError::runtime(format_args!(
401 "stdout not available in this host"
402 )))
403 }
404
405 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
406 {
407 use std::io::Write;
408 let stdout = std::io::stdout();
409 let mut handle = stdout.lock();
410 handle
411 .write_all(msg)
412 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
413 handle
414 .flush()
415 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
416 Ok(())
417 }
418 }
419
420 pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
429 let abs = abs_index(self, idx);
430 let v = index_to_value(self, abs);
431 let mt: Option<GcRef<LuaTable>> = match &v {
432 LuaValue::Table(t) => t.metatable(),
433 LuaValue::UserData(u) => u.metatable(),
434 _ => self.global().mt[v.base_type() as usize].clone(),
435 };
436 if let Some(mt_ref) = mt.as_ref() {
437 let key = self.intern_str(b"__tostring")?;
438 let f = mt_ref.get_short_str(&key);
439 if !matches!(f, LuaValue::Nil) {
440 let func_idx = self.top_idx();
441 self.push(f);
442 self.push(v.clone());
443 if self.current_ci().is_lua_code() {
444 self.do_call(func_idx, 1)?;
445 } else {
446 self.do_call_no_yield(func_idx, 1)?;
447 }
448 let top = self.top_idx();
449 let result = self.get_at(StackIdx(top.0 - 1));
450 if let LuaValue::Str(s) = result {
451 return Ok(s.as_bytes().to_vec());
452 }
453 let legacy_no_check = matches!(
454 self.global().lua_version,
455 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
456 );
457 if matches!(result, LuaValue::Int(_) | LuaValue::Float(_)) {
458 let s = crate::object::num_to_string(self, &result)?;
459 let out = s.as_bytes().to_vec();
460 if !matches!(self.global().lua_version, lua_types::LuaVersion::V51) {
461 let slot = StackIdx(top.0 - 1);
462 self.set_at(slot, LuaValue::Str(s));
463 }
464 return Ok(out);
465 }
466 if legacy_no_check {
467 let display_idx = StackIdx(top.0 - 1).0 as i32;
468 return display_default_bytes(self, &result, display_idx);
469 }
470 return Err(crate::debug::c_api_runtime(
471 self,
472 b"'__tostring' must return a string".to_vec(),
473 ));
474 }
475 }
476 let bytes: Vec<u8> = match &v {
477 LuaValue::Str(s) => {
478 let out = s.as_bytes().to_vec();
479 self.push(LuaValue::Str(s.clone()));
480 out
481 }
482 LuaValue::Int(_) | LuaValue::Float(_) => {
483 let s = crate::object::num_to_string(self, &v)?;
484 let out = s.as_bytes().to_vec();
485 self.push(LuaValue::Str(s));
486 out
487 }
488 LuaValue::Bool(b) => {
489 let lit: &[u8] = if *b { b"true" } else { b"false" };
490 let s = self.intern_str(lit)?;
491 self.push(LuaValue::Str(s));
492 lit.to_vec()
493 }
494 LuaValue::Nil => {
495 let s = self.intern_str(b"nil")?;
496 self.push(LuaValue::Str(s));
497 b"nil".to_vec()
498 }
499 _ => {
500 let named = if self.global().lua_version.honors_name_metafield() {
501 match &mt {
502 Some(mt_ref) => match mt_ref.get_str_bytes(b"__name") {
503 LuaValue::Str(s) => Some(s.as_bytes().to_vec()),
504 _ => None,
505 },
506 None => None,
507 }
508 } else {
509 None
510 };
511 let kind: Vec<u8> = match named {
512 Some(n) => n,
513 None => crate::tagmethods::type_name(v.base_type()).to_vec(),
514 };
515 let ptr = to_pointer(self, abs).unwrap_or(0);
516 let mut buf = kind;
517 buf.extend_from_slice(b": 0x");
518 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
519 let s = self.intern_str(&buf)?;
520 self.push(LuaValue::Str(s));
521 buf
522 }
523 };
524 Ok(bytes)
525 }
526
527 pub fn top(&mut self) -> i32 {
534 get_top(self)
535 }
536
537 pub fn get_top(&mut self) -> i32 {
541 get_top(self)
542 }
543
544 pub fn type_at(&mut self, idx: i32) -> LuaType {
549 lua_type_at(self, idx)
550 }
551
552 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
558 if lua_type_at(self, arg) == LuaType::None {
559 return Err(crate::debug::arg_error_impl(self, arg, b"value expected"));
560 }
561 Ok(())
562 }
563
564 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
574 match to_lua_string(self, arg)? {
575 Some(s) => Ok(s.as_bytes().to_vec()),
576 None => {
577 let got = index_to_value(self, arg);
578 let got_name = if lua_type_at(self, arg) == LuaType::None {
579 b"no value".to_vec()
580 } else {
581 crate::tagmethods::obj_type_name(self, &got)?
582 };
583 let extramsg = format!(
584 "string expected, got {}",
585 String::from_utf8_lossy(&got_name)
586 );
587 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
588 }
589 }
590 }
591
592 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
604 match to_integer_x(self, arg) {
605 Some(d) => Ok(d),
606 None => {
607 if is_number(self, arg) {
608 Err(crate::debug::arg_error_impl(
609 self,
610 arg,
611 b"number has no integer representation",
612 ))
613 } else {
614 let got = index_to_value(self, arg);
615 let got_name = if lua_type_at(self, arg) == LuaType::None {
616 b"no value".to_vec()
617 } else {
618 crate::tagmethods::obj_type_name(self, &got)?
619 };
620 let extramsg = format!(
621 "number expected, got {}",
622 String::from_utf8_lossy(&got_name)
623 );
624 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
625 }
626 }
627 }
628 }
629
630 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
640 match to_number_x(self, arg) {
641 Some(d) => Ok(d),
642 None => {
643 let got = index_to_value(self, arg);
644 let got_name = if lua_type_at(self, arg) == LuaType::None {
645 b"no value".to_vec()
646 } else {
647 crate::tagmethods::obj_type_name(self, &got)?
648 };
649 let extramsg = format!(
650 "number expected, got {}",
651 String::from_utf8_lossy(&got_name)
652 );
653 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
654 }
655 }
656 }
657
658 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
668 match lua_type_at(self, arg) {
669 LuaType::None | LuaType::Nil => Ok(def),
670 _ => match to_integer_x(self, arg) {
671 Some(d) => Ok(d),
672 None => {
673 if is_number(self, arg) {
674 Err(LuaError::arg_error(
675 arg,
676 "number has no integer representation",
677 ))
678 } else {
679 let got = index_to_value(self, arg);
680 Err(LuaError::type_arg_error(arg, "number", &got))
681 }
682 }
683 },
684 }
685 }
686
687 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
694 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
695 }
696
697 pub fn protected_call_k(
701 &mut self,
702 nargs: i32,
703 nresults: i32,
704 msgh: i32,
705 ctx: isize,
706 k: Option<crate::state::LuaKFunction>,
707 ) -> Result<(), LuaError> {
708 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
709 }
710
711 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
712 push_lstring(self, s)?;
713 Ok(())
714 }
715
716 pub fn push_c_closure(
717 &mut self,
718 f: fn(&mut LuaState) -> Result<usize, LuaError>,
719 n: i32,
720 ) -> Result<(), LuaError> {
721 push_cclosure(self, f, n)
722 }
723
724 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
725 raw_set_i(self, idx, n)
726 }
727
728 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
729 set_i(self, idx, n)
730 }
731
732 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
736 get_i_value(self, t, n)
737 }
738
739 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
743 set_i_value(self, t, n)
744 }
745
746 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
747 create_table(self, narr, nrec)
748 }
749
750 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
754 set_field(self, LUA_REGISTRYINDEX, key)
755 }
756
757 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
763 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
764 return Ok(false);
765 }
766 self.pop_n(1);
767 create_table(self, 0, 2)?;
768 push_lstring(self, tname)?;
769 set_field(self, -2, b"__name")?;
770 push_value(self, -1);
771 set_field(self, LUA_REGISTRYINDEX, tname)?;
772 Ok(true)
773 }
774
775 pub fn new_lib(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
782 create_table(self, 0, funcs.len() as i32)?;
783 for (name, f) in funcs {
784 push_cclosure(self, *f, 0)?;
785 set_field(self, -2, name)?;
786 }
787 Ok(())
788 }
789
790 pub fn register_lib(
796 &mut self,
797 _name: &[u8],
798 funcs: &[(&[u8], LuaCFunction)],
799 ) -> Result<(), LuaError> {
800 self.new_lib(funcs)
801 }
802
803 pub fn new_lib_table(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
812 create_table(self, 0, funcs.len() as i32)
813 }
814
815 pub fn set_funcs_with_upvalues(
820 &mut self,
821 funcs: &[(&[u8], LuaCFunction)],
822 nup: i32,
823 ) -> Result<(), LuaError> {
824 check_stack(self, nup);
825 for (name, f) in funcs {
826 for _ in 0..nup {
827 push_value(self, -nup);
828 }
829 push_cclosure(self, *f, nup)?;
830 set_field(self, -(nup + 2), name)?;
831 }
832 self.pop_n(nup as usize);
833 Ok(())
834 }
835
836 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
837 set_metatable(self, objindex)?;
838 Ok(())
839 }
840
841 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
847 get_field(self, LUA_REGISTRYINDEX, name)?;
848 set_metatable(self, -2)?;
849 Ok(())
850 }
851
852 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
856 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
857 return Ok(true);
858 }
859 self.pop_n(1);
860 let idx = abs_index(self, LUA_REGISTRYINDEX);
861 let new_tbl = self.new_table();
862 self.push(LuaValue::Table(new_tbl));
863 push_value(self, -1);
864 set_field(self, idx, name)?;
865 Ok(false)
866 }
867
868 pub fn new_userdata_typed(
878 &mut self,
879 _name: &[u8],
880 size: usize,
881 nuvalue: i32,
882 ) -> Result<GcRef<LuaUserData>, LuaError> {
883 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
884 let u = GcRef::new(LuaUserData {
888 data: vec![0u8; size].into_boxed_slice(),
889 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
890 metatable: std::cell::RefCell::new(None),
891 host_value: std::cell::RefCell::new(None),
892 });
893 u.account_buffer(u.buffer_bytes() as isize);
894 self.push(LuaValue::UserData(u.clone()));
895 self.gc_pre_collect_clear();
896 self.gc().check_step();
897 Ok(u)
898 }
899}
900
901pub fn value_identity_pointer(state: &LuaState, o: &LuaValue) -> Option<usize> {
911 match o {
912 LuaValue::Function(LuaClosure::LightC(f)) => state
913 .global()
914 .c_functions
915 .get(*f)
916 .and_then(|c| c.as_bare())
917 .map(|fp| fp as usize),
918 LuaValue::LightUserData(p) => Some(*p as usize),
919 LuaValue::Str(s) => Some(GcRef::identity(s)),
920 LuaValue::Table(t) => Some(GcRef::identity(t)),
921 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
922 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
923 LuaValue::UserData(u) => Some(GcRef::identity(u)),
924 LuaValue::Thread(t) => Some(GcRef::identity(t)),
925 _ => None,
926 }
927}
928
929fn display_default_bytes(
935 state: &mut LuaState,
936 val: &LuaValue,
937 _idx: i32,
938) -> Result<Vec<u8>, LuaError> {
939 match val {
940 LuaValue::Str(s) => Ok(s.as_bytes().to_vec()),
941 LuaValue::Int(_) | LuaValue::Float(_) => {
942 Ok(crate::object::num_to_string(state, val)?.as_bytes().to_vec())
943 }
944 LuaValue::Bool(b) => Ok(if *b { b"true".to_vec() } else { b"false".to_vec() }),
945 LuaValue::Nil => Ok(b"nil".to_vec()),
946 _ => {
947 let kind = crate::tagmethods::obj_type_name(state, val)?;
948 let ptr = value_identity_pointer(state, val).unwrap_or(0);
949 let mut buf = kind;
950 buf.extend_from_slice(b": 0x");
951 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
952 Ok(buf)
953 }
954 }
955}
956
957pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
960 if !is_valid_index(state, idx) {
961 return LuaType::None;
962 }
963 index_to_value(state, idx).base_type()
964}
965
966pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
967 t.type_name()
968}
969
970pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
971 let o = index_to_value(state, idx);
972 matches!(
973 o,
974 LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_))
975 )
976}
977
978pub fn is_integer(state: &LuaState, idx: i32) -> bool {
979 let o = index_to_value(state, idx);
980 matches!(o, LuaValue::Int(_))
981}
982
983pub fn is_number(state: &LuaState, idx: i32) -> bool {
984 let o = index_to_value(state, idx);
985 o.to_number_with_strconv().is_some()
986}
987
988pub fn is_string(state: &LuaState, idx: i32) -> bool {
989 let o = index_to_value(state, idx);
990 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
991}
992
993pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
994 let o = index_to_value(state, idx);
995 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
996}
997
998pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
999 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
1000 return false;
1001 }
1002 let o1 = index_to_value(state, index1);
1003 let o2 = index_to_value(state, index2);
1004 state.equal_obj(None, &o1, &o2)
1005}
1006
1007pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
1009 const LUA_OPUNM: i32 = 12;
1010 const LUA_OPBNOT: i32 = 14;
1011 if op == LUA_OPUNM || op == LUA_OPBNOT {
1012 let top_val = state.get_at(state.top_idx() - 1);
1014 state.push(top_val);
1015 }
1016 let top = state.top_idx();
1017 let a = state.get_at(top - 2);
1018 let b = state.get_at(top - 1);
1019 let result = state.arith_op(op, &a, &b)?;
1020 state.set_at(top - 2, result);
1021 state.pop();
1022 Ok(())
1023}
1024
1025pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
1026 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
1027 let o1 = index_to_value(state, index1);
1028 let o2 = index_to_value(state, index2);
1029 if valid {
1030 match op {
1031 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
1032 1 => state.less_than(&o1, &o2),
1033 2 => state.less_equal(&o1, &o2),
1034 _ => {
1035 debug_assert!(false, "invalid option");
1036 Ok(false)
1037 }
1038 }
1039 } else {
1040 Ok(false)
1041 }
1042}
1043
1044pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
1045 match state.str_to_num(s) {
1046 Some((val, consumed)) => {
1047 state.push(val);
1048 consumed
1049 }
1050 None => 0,
1051 }
1052}
1053
1054pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
1055 let o = index_to_value(state, idx);
1056 o.to_number_with_strconv()
1057}
1058
1059pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
1060 let o = index_to_value(state, idx);
1061 o.to_integer_with_strconv()
1062}
1063
1064pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
1065 let o = index_to_value(state, idx);
1066 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
1067}
1068
1069pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Option<GcRef<LuaString>>, LuaError> {
1071 let o = index_to_value(state, idx);
1072 if let LuaValue::Str(s) = &o {
1073 return Ok(Some(s.clone()));
1074 }
1075 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
1076 return Ok(None);
1077 }
1078 state.obj_to_string(idx)?;
1079 state.gc_pre_collect_clear();
1080 state.gc().check_step();
1081 let updated = index_to_value(state, idx);
1082 if let LuaValue::Str(s) = updated {
1083 Ok(Some(s))
1084 } else {
1085 Ok(None)
1086 }
1087}
1088
1089pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
1090 let o = index_to_value(state, idx);
1091 match &o {
1092 LuaValue::Str(s) => s.len() as u64,
1093 LuaValue::UserData(u) => u.len() as u64,
1094 LuaValue::Table(t) => state.table_getn(t) as u64,
1095 _ => 0,
1096 }
1097}
1098
1099pub fn to_cfunction(
1104 state: &LuaState,
1105 idx: i32,
1106) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
1107 let o = index_to_value(state, idx);
1108 let cfn_idx = match o {
1109 LuaValue::Function(LuaClosure::LightC(i)) => i,
1110 LuaValue::Function(LuaClosure::C(ccl)) => ccl.func,
1111 _ => return None,
1112 };
1113 state.global().c_functions.get(cfn_idx).and_then(|c| c.as_bare())
1114}
1115
1116#[inline]
1117fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1118 match o {
1119 LuaValue::UserData(u) => {
1120 let _ = u;
1124 None
1125 }
1126 LuaValue::LightUserData(p) => Some(*p),
1127 _ => None,
1128 }
1129}
1130
1131pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1132 let o = index_to_value(state, idx);
1133 to_userdata_ptr(&o)
1134}
1135
1136pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1137 let o = index_to_value(state, idx);
1141 if let LuaValue::Thread(t) = o {
1142 Some(t)
1143 } else {
1144 None
1145 }
1146}
1147
1148pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1152 let o = index_to_value(state, idx);
1153 value_identity_pointer(state, &o)
1154}
1155
1156pub fn push_nil(state: &mut LuaState) {
1159 state.push(LuaValue::Nil);
1160}
1161
1162pub fn push_number(state: &mut LuaState, n: f64) {
1163 state.push(LuaValue::Float(n));
1164}
1165
1166pub fn push_integer(state: &mut LuaState, n: i64) {
1167 state.push(LuaValue::Int(n));
1168}
1169
1170pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1172 let ts = state.intern_str(s)?;
1173 state.push(LuaValue::Str(ts.clone()));
1174 state.gc_pre_collect_clear();
1175 state.gc().check_step();
1176 Ok(ts)
1177}
1178
1179pub fn push_string(
1180 state: &mut LuaState,
1181 s: Option<&[u8]>,
1182) -> Result<Option<GcRef<LuaString>>, LuaError> {
1183 match s {
1184 None => {
1185 state.push(LuaValue::Nil);
1186 state.gc_pre_collect_clear();
1187 state.gc().check_step();
1188 Ok(None)
1189 }
1190 Some(bytes) => {
1191 let ts = state.intern_str(bytes)?;
1192 state.push(LuaValue::Str(ts.clone()));
1193 state.gc_pre_collect_clear();
1194 state.gc().check_step();
1195 Ok(Some(ts))
1196 }
1197 }
1198}
1199
1200pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1204 let ts = state.intern_str(formatted)?;
1205 state.push(LuaValue::Str(ts.clone()));
1206 state.gc_pre_collect_clear();
1207 state.gc().check_step();
1208 Ok(ts)
1209}
1210
1211pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1213 push_vfstring(state, formatted)
1214}
1215
1216pub fn push_cclosure(
1225 state: &mut LuaState,
1226 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1227 n: i32,
1228) -> Result<(), LuaError> {
1229 let idx: lua_types::closure::LuaCFnPtr = state.register_c_function(f, n == 0);
1230 if n == 0 {
1231 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1232 } else {
1233 debug_assert!(
1234 n > 0 && (n as u32) <= MAX_UPVAL as u32,
1235 "upvalue index too large"
1236 );
1237 let n_usize = n as usize;
1238 let top = state.top_idx();
1239 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1240 let base = top.0 as usize - n_usize;
1241 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1242 for i in 0..n_usize {
1243 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1244 }
1245 state.pop_n(n_usize);
1246 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1247 func: idx,
1248 upvalues: std::cell::RefCell::new(upvalues),
1249 }));
1250 if let LuaClosure::C(ccl) = &cl {
1251 ccl.account_buffer(ccl.buffer_bytes() as isize);
1252 }
1253 state.push(LuaValue::Function(cl));
1254 state.gc_pre_collect_clear();
1255 state.gc().check_step();
1256 }
1257 Ok(())
1258}
1259
1260pub fn push_boolean(state: &mut LuaState, b: bool) {
1261 state.push(LuaValue::Bool(b));
1262}
1263
1264pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1265 state.push(LuaValue::LightUserData(p));
1266}
1267
1268pub fn push_thread(state: &mut LuaState) -> bool {
1270 let (value, is_main) = {
1271 let g = state.global();
1272 let id = g.current_thread_id;
1273 let v = g
1274 .thread_value_for(id)
1275 .expect("current_thread_id must always resolve to a registered thread");
1276 (v, id == g.main_thread_id)
1277 };
1278 state.push(LuaValue::Thread(value));
1279 is_main
1280}
1281
1282fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1285 let str_val = {
1286 let ts = state.intern_str(k)?;
1287 LuaValue::Str(ts)
1288 };
1289 let result = state.table_get_with_tm(&t, &str_val)?;
1290 state.push(result);
1291 let top = state.top_idx();
1292 Ok(state.get_at(top - 1).base_type())
1293}
1294
1295fn get_global_table(state: &LuaState) -> LuaValue {
1296 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1306 let running = state.global().current_thread_id;
1307 return state.v51_thread_lgt(running);
1308 }
1309 state.global().globals.clone()
1310}
1311
1312pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1313 let g = get_global_table(state);
1314 aux_get_str(state, g, name)
1315}
1316
1317pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1318 let t = index_to_value(state, idx);
1319 let top = state.top_idx();
1320 let key = state.get_at(top - 1);
1321 let result = state.table_get_with_tm(&t, &key)?;
1322 state.set_at(top - 1, result);
1323 let val = state.get_at(top - 1);
1324 Ok(val.base_type())
1325}
1326
1327pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1328 let t = index_to_value(state, idx);
1329 aux_get_str(state, t, k)
1330}
1331
1332pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1333 let t = index_to_value(state, idx);
1334 let key = LuaValue::Int(n);
1335 let result = state.table_get_with_tm(&t, &key)?;
1336 state.push(result);
1337 let top = state.top_idx();
1338 Ok(state.get_at(top - 1).base_type())
1339}
1340
1341pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1347 let key = LuaValue::Int(n);
1348 let result = state.table_get_with_tm(t, &key)?;
1349 state.push(result);
1350 let top = state.top_idx();
1351 Ok(state.get_at(top - 1).base_type())
1352}
1353
1354fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1355 let v = val.unwrap_or(LuaValue::Nil);
1356 state.push(v);
1357 let top = state.top_idx();
1358 state.get_at(top - 1).base_type()
1359}
1360
1361fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1362 let t = index_to_value(state, idx);
1363 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1364 if let LuaValue::Table(tbl) = t {
1365 Some(tbl)
1366 } else {
1367 None
1368 }
1369}
1370
1371pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1372 let t = get_table_value(state, idx);
1373 let top = state.top_idx();
1374 let key = state.get_at(top - 1);
1375 let val = t.as_ref().map(|tbl| tbl.get(&key));
1376 state.set_top_idx(top - 1);
1377 finish_raw_get(state, val)
1378}
1379
1380pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1381 let t = get_table_value(state, idx);
1382 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1383 finish_raw_get(state, val)
1384}
1385
1386pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1387 let t = get_table_value(state, idx);
1388 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1389 let val = t.as_ref().map(|tbl| tbl.get(&key));
1390 finish_raw_get(state, val)
1391}
1392
1393pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1394 let t = state.new_table();
1395 if narray > 0 || nrec > 0 {
1396 t.resize(state, narray as usize, nrec as usize)?;
1397 }
1398 state.push(LuaValue::Table(t));
1399 state.gc_pre_collect_clear();
1400 state.gc().check_step();
1401 Ok(())
1402}
1403
1404pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1405 let obj = index_to_value(state, objindex);
1406 let mt: Option<GcRef<LuaTable>> = match &obj {
1407 LuaValue::Table(t) => t.metatable(),
1408 LuaValue::UserData(u) => u.metatable(),
1409 other => {
1410 let idx = other.base_type() as usize;
1411 state.global().mt[idx].clone()
1412 }
1413 };
1414 if let Some(mt_table) = mt {
1415 state.push(LuaValue::Table(mt_table));
1416 true
1417 } else {
1418 false
1419 }
1420}
1421
1422pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1423 let o = index_to_value(state, idx);
1424 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1425 if let LuaValue::UserData(ref u) = o {
1426 let uv = u.uv.borrow();
1427 let uv_count = uv.len() as i32;
1428 if n <= 0 || n > uv_count {
1429 state.push(LuaValue::Nil);
1430 LuaType::None
1431 } else {
1432 let val = uv[(n - 1) as usize].clone();
1433 let t = val.base_type();
1434 state.push(val);
1435 t
1436 }
1437 } else {
1438 state.push(LuaValue::Nil);
1439 LuaType::None
1440 }
1441}
1442
1443fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1446 let str_val = {
1447 let ts = state.intern_str(k)?;
1448 LuaValue::Str(ts)
1449 };
1450 let top = state.top_idx();
1455 let val = state.get_at(top - 1);
1456 state.table_set_with_tm(&t, str_val, val)?;
1457 state.pop();
1458 Ok(())
1459}
1460
1461pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1462 let g = get_global_table(state);
1463 aux_set_str(state, g, name)
1464}
1465
1466pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1467 let t = index_to_value(state, idx);
1468 let top = state.top_idx();
1469 let key = state.get_at(top - 2);
1470 let val = state.get_at(top - 1);
1471 state.table_set_with_tm(&t, key, val)?;
1472 state.set_top_idx(top - 2);
1473 Ok(())
1474}
1475
1476pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1477 let t = index_to_value(state, idx);
1478 aux_set_str(state, t, k)
1479}
1480
1481pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1482 let t = index_to_value(state, idx);
1483 let top = state.top_idx();
1484 let val = state.get_at(top - 1);
1485 let key = LuaValue::Int(n);
1486 state.table_set_with_tm(&t, key, val)?;
1487 state.pop();
1488 Ok(())
1489}
1490
1491pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1497 let top = state.top_idx();
1498 let val = state.get_at(top - 1);
1499 let key = LuaValue::Int(n);
1500 state.table_set_with_tm(t, key, val)?;
1501 state.pop();
1502 Ok(())
1503}
1504
1505fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1506 let t = get_table_value(state, idx)
1507 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1508 let top = state.top_idx();
1509 let val = state.get_at(top - 1);
1510 t.raw_set(state, key, val)?;
1511 t.invalidate_tm_cache();
1512 let top_val = state.get_at(top - 1);
1513 state.gc().table_barrier_back(&t, &top_val);
1514 state.set_top_idx(top - n as i32);
1515 Ok(())
1516}
1517
1518pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1519 let top = state.top_idx();
1520 let key = state.get_at(top - 2);
1521 aux_raw_set(state, idx, key, 2)
1522}
1523
1524pub fn raw_set_p(
1525 state: &mut LuaState,
1526 idx: i32,
1527 p: *const core::ffi::c_void,
1528) -> Result<(), LuaError> {
1529 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1530 aux_raw_set(state, idx, key, 1)
1531}
1532
1533pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1534 let t = get_table_value(state, idx)
1535 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1536 let top = state.top_idx();
1537 let val = state.get_at(top - 1);
1538 t.raw_set_int(state, n, val)?;
1539 let top_val = state.get_at(top - 1);
1540 state.gc().table_barrier_back(&t, &top_val);
1541 state.pop();
1542 Ok(())
1543}
1544
1545fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1550 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1551 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1552}
1553
1554fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1555 let heap_ptr = object.heap_ptr();
1556 let mut g = state.global_mut();
1557 if g.finalizers.push_pending_unique(object) {
1558 if let Some(ptr) = heap_ptr {
1559 g.heap.move_allgc_to_finobj(ptr);
1560 }
1561 }
1562}
1563
1564struct Udata51RosterEntry {
1579 weak: lua_types::gc::GcWeak<LuaUserData>,
1580 submitted: std::cell::Cell<bool>,
1581}
1582
1583impl lua_gc::Udata51Probe for Udata51RosterEntry {
1584 fn is_alive(&self) -> bool {
1585 self.weak.upgrade().is_some()
1586 }
1587
1588 fn as_any(&self) -> &dyn std::any::Any {
1589 self
1590 }
1591}
1592
1593fn roster_v51_userdata(state: &LuaState, ud: &GcRef<LuaUserData>) {
1603 if !matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1604 return;
1605 }
1606 let probe = std::rc::Rc::new(Udata51RosterEntry {
1607 weak: ud.downgrade(),
1608 submitted: std::cell::Cell::new(false),
1609 });
1610 state.global().heap.register_v51_udata(probe);
1611}
1612
1613pub fn scan_v51_finalizable_userdata(state: &mut LuaState) {
1625 if !matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1626 return;
1627 }
1628 let probes = state.global().heap.scan_v51_finalizable();
1629 if probes.is_empty() {
1630 return;
1631 }
1632 let gc_name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1633 for probe in &probes {
1634 let entry = match probe.as_any().downcast_ref::<Udata51RosterEntry>() {
1635 Some(entry) => entry,
1636 None => continue,
1637 };
1638 if entry.submitted.get() {
1639 continue;
1640 }
1641 let ud = match entry.weak.upgrade() {
1642 Some(ud) => ud,
1643 None => continue,
1644 };
1645 let has_gc = match ud.metatable() {
1646 Some(mt) => !matches!(mt.get_short_str(&gc_name), LuaValue::Nil),
1647 None => false,
1648 };
1649 if has_gc {
1650 entry.submitted.set(true);
1651 register_finalizable_object(state, FinalizerObject::UserData(ud));
1652 }
1653 }
1654}
1655
1656pub fn run_pending_finalizers(state: &mut LuaState) {
1659 let _ = run_pending_finalizers_inner(state, false);
1660}
1661
1662const GC_FIN_MAX: usize = 10;
1663
1664pub fn run_pending_finalizers_inner(state: &mut LuaState, propagate: bool) -> Result<(), LuaError> {
1684 run_pending_finalizers_limited(state, propagate, None).map(|_| ())
1685}
1686
1687pub(crate) fn run_some_pending_finalizers_inner(
1688 state: &mut LuaState,
1689 propagate: bool,
1690) -> Result<usize, LuaError> {
1691 run_pending_finalizers_limited(state, propagate, Some(GC_FIN_MAX))
1692}
1693
1694fn run_pending_finalizers_limited(
1695 state: &mut LuaState,
1696 propagate: bool,
1697 limit: Option<usize>,
1698) -> Result<usize, LuaError> {
1699 let version = state.global().lua_version;
1700 let mut processed = 0usize;
1701 loop {
1702 if limit.map_or(false, |limit| processed >= limit) {
1703 break;
1704 }
1705 if !state.global().finalizers.has_to_be_finalized() {
1709 break;
1710 }
1711 let object = {
1721 let mut g = state.global_mut();
1722 let object = g
1723 .finalizers
1724 .pop_to_be_finalized()
1725 .expect("to-be-finalized checked non-empty");
1726 if let Some(ptr) = object.heap_ptr() {
1727 g.heap.move_tobefnz_to_allgc(ptr);
1728 }
1729 object
1730 };
1731 processed += 1;
1732 let mt = object.metatable();
1733 let gc_fn = match mt {
1734 Some(ref m) => {
1735 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1736 m.get_short_str(&name)
1737 }
1738 None => LuaValue::Nil,
1739 };
1740 if !matches!(gc_fn, LuaValue::Function(_)) {
1741 continue;
1742 }
1743 let saved_top = state.top_idx();
1744 let ci_top = state.current_call_info().top;
1745 if saved_top.0 < ci_top.0 {
1746 state.clear_stack_range(saved_top, ci_top);
1747 state.set_top(ci_top);
1748 }
1749 state.push(gc_fn);
1750 state.push(object.as_lua_value());
1751 let func_idx = state.top_idx() - 2;
1752 let _heap_guard = {
1753 let g = state.global.borrow();
1754 lua_gc::HeapGuard::push(&g.heap)
1755 };
1756 let old_allowhook = state.allowhook;
1757 let old_gcstp = state.global_mut().stop_gc_internal();
1758 state.allowhook = false;
1759 let caller_ci = state.ci;
1760 let caller_status = state.get_ci(caller_ci).callstatus;
1761 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1762 let status = crate::do_::pcall(state, |s| s.call_no_yield(func_idx, 0), func_idx, 0);
1763 let finalizer_error: Option<LuaValue> = if status != LuaStatus::Ok {
1767 Some(state.get_at(state.top_idx() - 1).clone())
1768 } else {
1769 None
1770 };
1771 state.get_ci_mut(caller_ci).callstatus = caller_status;
1772 state.allowhook = old_allowhook;
1773 state.global_mut().set_gc_stop_flags(old_gcstp);
1774 state.set_top(saved_top);
1775
1776 if let Some(errobj) = finalizer_error {
1777 match version {
1778 lua_types::LuaVersion::V51 if propagate => {
1779 return Err(LuaError::from_value(errobj));
1786 }
1787 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1788 let msg: Vec<u8> = match &errobj {
1795 LuaValue::Str(s) => s.as_bytes().to_vec(),
1796 _ => b"no message".to_vec(),
1797 };
1798 let mut wrapped = b"error in __gc metamethod (".to_vec();
1799 wrapped.extend_from_slice(&msg);
1800 wrapped.push(b')');
1801 let interned = state.intern_str(&wrapped)?;
1802 return Err(LuaError::from_value(LuaValue::Str(interned)));
1803 }
1804 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1805 let msg: Vec<u8> = match &errobj {
1809 LuaValue::Str(s) => s.as_bytes().to_vec(),
1810 _ => b"error object is not a string".to_vec(),
1811 };
1812 state.emit_warning(b"error in ", true);
1813 state.emit_warning(b"__gc", true);
1814 state.emit_warning(b" (", true);
1815 state.emit_warning(&msg, true);
1816 state.emit_warning(b")", false);
1817 }
1818 _ => {}
1819 }
1820 }
1821 }
1822 Ok(processed)
1826}
1827
1828const CLOSE_FINALIZER_MAX_PASSES: usize = 1000;
1836
1837pub fn run_close_finalizers(state: &mut LuaState) {
1864 let mut seen = std::collections::HashSet::<usize>::new();
1865 {
1866 let g = state.global();
1867 for object in g.finalizers.to_be_finalized() {
1868 seen.insert(object.identity());
1869 }
1870 }
1871 let mut passes = 0usize;
1872 loop {
1873 {
1874 let g = state.global();
1875 if g.finalizers.pending_len() == 0 && !g.finalizers.has_to_be_finalized() {
1876 break;
1877 }
1878 }
1879 passes += 1;
1880 if passes > CLOSE_FINALIZER_MAX_PASSES {
1881 break;
1882 }
1883 let pending: Vec<FinalizerObject> = state.global_mut().finalizers.take_pending();
1884 {
1885 let mut g = state.global_mut();
1886 for object in pending.into_iter().rev() {
1887 let heap_ptr = object.heap_ptr();
1888 if seen.insert(object.identity()) {
1889 g.finalizers.push_to_be_finalized(object);
1890 if let Some(ptr) = heap_ptr {
1891 g.heap.move_finobj_to_tobefnz(ptr);
1892 }
1893 }
1894 }
1895 }
1896 run_pending_finalizers(state);
1897 }
1898}
1899
1900fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1906 let mut g = state.global_mut();
1907 g.weak_tables_registry.live_snapshot()
1908}
1909
1910pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1911 let top = state.top_idx();
1912 let mt_val = state.get_at(top - 1);
1913 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1914 None
1915 } else {
1916 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1917 if let LuaValue::Table(t) = mt_val {
1918 Some(t)
1919 } else {
1920 None
1921 }
1922 };
1923
1924 let obj = index_to_value(state, objindex);
1925 match obj {
1926 LuaValue::Table(ref tbl) => {
1927 if mt.is_some() {
1928 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1929 }
1930 tbl.set_metatable(mt.clone());
1931 if tbl.weak_mode() == 0 {
1932 state
1933 .global_mut()
1934 .weak_tables_registry
1935 .remove_identity(tbl.identity());
1936 } else {
1937 state
1938 .global_mut()
1939 .weak_tables_registry
1940 .push_unique(WeakTableEntry::new(tbl));
1941 }
1942 let tables_finalizable =
1952 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1953 if tables_finalizable {
1954 if let Some(ref mt_table) = mt {
1955 if metatable_has_gc(state, mt_table) {
1956 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1957 }
1958 }
1959 }
1960 }
1961 LuaValue::UserData(ref ud) => {
1962 if let Some(ref mt_table) = mt {
1963 state.gc().obj_barrier(ud, mt_table);
1964 if metatable_has_gc(state, mt_table) {
1965 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1966 }
1967 roster_v51_userdata(state, ud);
1972 }
1973 ud.set_metatable(mt);
1974 }
1975 ref other => {
1976 let idx = other.base_type() as usize;
1977 state.global_mut().mt[idx] = mt;
1978 }
1979 }
1980 state.pop();
1981 Ok(true)
1982}
1983
1984pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1985 let o = index_to_value(state, idx);
1986 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1987 let top = state.top_idx();
1988 let val = state.get_at(top - 1);
1989 let res = if let LuaValue::UserData(ref ud) = o {
1990 let mut uv = ud.uv.borrow_mut();
1991 let nuvalue = uv.len() as i32;
1992 if n < 1 || n > nuvalue {
1993 false
1994 } else {
1995 uv[(n - 1) as usize] = val.clone();
1996 drop(uv);
1997 state.gc().barrier_back(ud, &val);
1998 true
1999 }
2000 } else {
2001 false
2002 };
2003 state.pop();
2004 Ok(res)
2005}
2006
2007pub fn call_k(
2011 state: &mut LuaState,
2012 nargs: i32,
2013 nresults: i32,
2014 ctx: isize,
2015 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
2016) -> Result<(), LuaError> {
2017 let top = state.top_idx();
2018 let func_idx = top - (nargs + 1);
2019 if k.is_some() && state.is_yieldable() {
2025 let ci_idx = state.ci;
2026 {
2027 let ci = state.get_ci_mut(ci_idx);
2028 ci.set_u_c_k(k);
2029 ci.set_u_c_ctx(ctx);
2030 }
2031 state.call_at(func_idx, nresults)?;
2032 } else {
2033 state.call_no_yield(func_idx, nresults)?;
2034 }
2035 state.adjust_results(nresults);
2036 Ok(())
2037}
2038
2039pub fn pcall_k(
2040 state: &mut LuaState,
2041 nargs: i32,
2042 nresults: i32,
2043 errfunc: i32,
2044 ctx: isize,
2045 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
2046) -> Result<LuaStatus, LuaError> {
2047 let _heap_guard = {
2052 let g = state.global.borrow();
2053 lua_gc::HeapGuard::push(&g.heap)
2058 };
2059 let err_handler_idx: isize = if errfunc == 0 {
2060 0
2061 } else {
2062 let o = index_to_stack_idx(state, errfunc);
2063 debug_assert!(
2064 matches!(state.get_at(o), LuaValue::Function(_)),
2065 "error handler must be a function"
2066 );
2067 o.0 as isize
2068 };
2069 let top = state.top_idx();
2070 let func_idx = top - (nargs + 1);
2071 if k.is_none() || !state.is_yieldable() {
2072 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
2073 state.adjust_results(nresults);
2074 return Ok(LuaStatus::Ok);
2075 }
2076 let ci_idx = state.ci;
2082 let allow = state.allowhook;
2083 let saved_errfunc = state.errfunc;
2084 {
2085 let ci = state.get_ci_mut(ci_idx);
2086 ci.set_u_c_k(k);
2087 ci.set_u_c_ctx(ctx);
2088 ci.set_u2_funcidx(func_idx.0 as i32);
2089 ci.set_u_c_old_errfunc(saved_errfunc);
2090 ci.set_oah(allow);
2091 ci.callstatus |= crate::state::CIST_YPCALL;
2092 }
2093 state.errfunc = err_handler_idx;
2094 let call_result = crate::do_::call(state, func_idx, nresults);
2095 match call_result {
2096 Ok(()) => {
2097 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
2100 state.errfunc = saved_errfunc;
2101 state.adjust_results(nresults);
2102 Ok(LuaStatus::Ok)
2103 }
2104 Err(crate::state::LuaError::Yield) => {
2105 Err(crate::state::LuaError::Yield)
2109 }
2110 Err(e) => {
2111 Err(e)
2115 }
2116 }
2117}
2118
2119fn first_upvalue_is_env(lcl: &lua_types::closure::LuaLClosure) -> bool {
2133 lcl.proto
2134 .upvalues
2135 .first()
2136 .and_then(|uv| uv.name.as_ref())
2137 .is_some_and(|s| s.as_bytes() == b"_ENV")
2138}
2139
2140pub fn load(
2141 state: &mut LuaState,
2142 reader: crate::zio::ChunkReader,
2143 chunkname: Option<&[u8]>,
2144 mode: Option<&[u8]>,
2145) -> Result<LuaStatus, LuaError> {
2146 let _heap_guard = {
2154 let g = state.global.borrow();
2155 lua_gc::HeapGuard::push(&g.heap)
2156 };
2157 let name = chunkname.unwrap_or(b"?");
2158 let z = crate::zio::ZIO::new(reader);
2159 let status = state.protected_parser(z, name, mode);
2160 if status == LuaStatus::Ok {
2161 let top = state.top_idx();
2162 let func_val = state.get_at(top - 1);
2163 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
2164 let inject_env = match state.global().lua_version {
2165 lua_types::LuaVersion::V51 => first_upvalue_is_env(&lcl),
2166 lua_types::LuaVersion::V52 => lcl.upvals.len() == 1,
2167 _ => !lcl.upvals.is_empty(),
2168 };
2169 if inject_env {
2170 let gt = get_global_table(state);
2171 let uv = state.new_upval_closed(gt);
2172 lcl.set_upval(0, uv);
2173 state.gc().obj_barrier(&lcl, &uv);
2174 }
2175 }
2176 }
2177 Ok(status)
2178}
2179
2180pub fn dump(
2181 state: &LuaState,
2182 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
2183 strip: bool,
2184) -> Result<bool, LuaError> {
2185 let top = state.top_idx();
2186 let o = state.get_at(top - 1);
2187 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
2188 crate::dump::dump(state, &lcl.proto, writer, strip)?;
2189 Ok(true)
2190 } else {
2191 Ok(false)
2192 }
2193}
2194
2195pub fn status(state: &LuaState) -> LuaStatus {
2196 LuaStatus::from_raw(state.status as i32)
2197}
2198
2199#[repr(i32)]
2203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2204pub enum GcWhat {
2205 Stop = 0,
2206 Restart = 1,
2207 Collect = 2,
2208 Count = 3,
2209 CountB = 4,
2210 Step = 5,
2211 SetPause = 6,
2212 SetStepMul = 7,
2213 IsRunning = 9,
2214 Gen = 10,
2215 Inc = 11,
2216}
2217
2218pub enum GcArgs {
2220 Stop,
2221 Restart,
2222 Collect,
2223 Count,
2224 CountB,
2225 Step {
2226 data: i32,
2227 },
2228 SetPause {
2229 value: i32,
2230 },
2231 SetStepMul {
2232 value: i32,
2233 },
2234 IsRunning,
2235 Gen {
2236 minormul: i32,
2237 majormul: i32,
2238 },
2239 Inc {
2240 pause: i32,
2241 stepmul: i32,
2242 stepsize: i32,
2243 },
2244 Param {
2247 param: usize,
2248 value: i64,
2249 },
2250}
2251
2252pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
2253 if let GcArgs::Param { param, value } = &args {
2257 return state.global_mut().gc55_param(*param, *value) as i32;
2258 }
2259 if state.global().is_gc_stopped_internally() {
2260 return -1;
2261 }
2262 match args {
2263 GcArgs::Stop => {
2264 state.global_mut().set_gc_stop_user();
2265 }
2266 GcArgs::Restart => {
2267 {
2268 let mut g = state.global_mut();
2269 crate::state::set_debt(&mut *g, 0);
2270 }
2271 state.global_mut().clear_gc_stop();
2272 }
2273 GcArgs::Collect => {
2274 if !state.allowhook {
2275 return 0;
2276 }
2277 scan_v51_finalizable_userdata(state);
2281 state.gc().full_collect();
2287 if let Err(e) = run_pending_finalizers_inner(state, true) {
2295 state.global_mut().gc_finalizer_error = Some(e.into_value());
2296 }
2297 }
2298 GcArgs::Count => {
2299 let g = state.global();
2300 let total = g.heap.bytes_used();
2301 return (total >> 10) as i32;
2302 }
2303 GcArgs::CountB => {
2304 let g = state.global();
2305 let total = g.heap.bytes_used();
2306 return (total & 0x3ff) as i32;
2307 }
2308 GcArgs::Step { data } => {
2309 let old_stp = {
2310 let mut g = state.global_mut();
2311 let old = g.gc_stop_flags();
2312 g.clear_gc_stop();
2313 old
2314 };
2315 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2322 let work_units = if data == 0 {
2323 stepmul
2324 } else {
2325 let raw = (data as isize).saturating_mul(stepmul);
2326 raw.max(1)
2327 };
2328 let debt_for_result = if data == 0 {
2329 let mut g = state.global_mut();
2330 crate::state::set_debt(&mut *g, 0);
2331 0
2332 } else {
2333 let debt = data as isize * 1024 + state.global().gc_debt();
2334 let mut g = state.global_mut();
2335 crate::state::set_debt(&mut *g, debt);
2336 debt
2337 };
2338 let gen_mode = state.global().is_gen_mode();
2339 let cycle_complete = if gen_mode {
2340 if data == 0 {
2341 state.gc().generational_step_minor_only();
2342 } else {
2343 state.gc().generational_step();
2344 }
2345 if state.global().finalizers.has_to_be_finalized() {
2346 if let Err(e) = run_pending_finalizers_inner(state, true) {
2347 state.global_mut().gc_finalizer_error = Some(e.into_value());
2348 }
2349 }
2350 debt_for_result > 0 && state.global().gc_at_pause()
2351 } else if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2352 && state.global().finalizers.has_to_be_finalized()
2353 {
2354 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2355 state.global_mut().gc_finalizer_error = Some(e.into_value());
2356 }
2357 if state.global().finalizers.has_to_be_finalized() {
2358 false
2359 } else {
2360 state.global().heap.finish_callfin_phase()
2361 }
2362 } else {
2363 let completed = state.gc().incremental_step(work_units);
2364 if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2365 && state.global().finalizers.has_to_be_finalized()
2366 {
2367 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2368 state.global_mut().gc_finalizer_error = Some(e.into_value());
2369 }
2370 if state.global().finalizers.has_to_be_finalized() {
2371 false
2372 } else {
2373 state.global().heap.finish_callfin_phase()
2374 }
2375 } else {
2376 completed
2377 }
2378 };
2379 state.global_mut().set_gc_stop_flags(old_stp);
2380 {
2382 let heap_state = state.global().heap.gc_state();
2383 let mut g = state.global_mut();
2384 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2385 }
2386 return if cycle_complete { 1 } else { 0 };
2387 }
2388 GcArgs::SetPause { value } => {
2389 let old = state.global().gc_pause_param();
2390 state.global_mut().set_gc_pause_param(value);
2391 return old;
2392 }
2393 GcArgs::SetStepMul { value } => {
2394 let old = state.global().gc_stepmul_param();
2395 state.global_mut().set_gc_stepmul_param(value);
2396 return old;
2397 }
2398 GcArgs::IsRunning => {
2399 return state.global().gc_running() as i32;
2400 }
2401 GcArgs::Gen { minormul, majormul } => {
2402 let old_mode = if state.global().is_gen_mode() {
2403 10i32
2404 } else {
2405 11i32
2406 };
2407 if minormul != 0 {
2408 state.global_mut().genminormul = minormul as u8;
2409 }
2410 if majormul != 0 {
2411 state.global_mut().set_gc_genmajormul(majormul);
2412 }
2413 state.gc().change_mode(crate::state::GcKind::Generational);
2414 return old_mode;
2415 }
2416 GcArgs::Inc {
2417 pause,
2418 stepmul,
2419 stepsize,
2420 } => {
2421 let old_mode = if state.global().is_gen_mode() {
2422 10i32
2423 } else {
2424 11i32
2425 };
2426 if pause != 0 {
2427 state.global_mut().set_gc_pause_param(pause);
2428 }
2429 if stepmul != 0 {
2430 state.global_mut().set_gc_stepmul_param(stepmul);
2431 }
2432 if stepsize != 0 {
2433 state.global_mut().gcstepsize = stepsize as u8;
2434 }
2435 state.gc().change_mode(crate::state::GcKind::Incremental);
2436 return old_mode;
2437 }
2438 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2439 }
2440 0
2441}
2442
2443pub fn configure_startup_gc_mode(state: &mut LuaState) {
2450 if matches!(
2451 state.global().lua_version,
2452 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2453 ) {
2454 let _ = gc(state, GcArgs::Restart);
2455 let _ = gc(
2456 state,
2457 GcArgs::Gen {
2458 minormul: 0,
2459 majormul: 0,
2460 },
2461 );
2462 }
2463}
2464
2465pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2471 let top = state.top_idx();
2472 let errobj = state.get_at(top - 1);
2473 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2474 let memerr = state.global().memerrmsg.clone();
2475 GcRef::ptr_eq(s, &memerr)
2476 } else {
2477 false
2478 };
2479 if is_mem_err {
2480 Err(LuaError::Memory)
2481 } else {
2482 Err(LuaError::from_value(errobj))
2483 }
2484}
2485
2486fn normalize_float_only_next_key(state: &LuaState, key: LuaValue) -> LuaValue {
2496 if state.global().lua_version.number_model() != lua_types::NumberModel::FloatOnly {
2497 return key;
2498 }
2499 if let LuaValue::Float(f) = key {
2500 let k = f as i64;
2501 if k as f64 == f {
2502 return LuaValue::Int(k);
2503 }
2504 }
2505 key
2506}
2507
2508pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2509 let t = get_table_value(state, idx)
2510 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2511 let top = state.top_idx();
2512 let key = normalize_float_only_next_key(state, state.get_at(top - 1));
2513 match t.next(key)? {
2514 Some((next_key, next_val)) => {
2515 state.set_at(top - 1, next_key);
2516 state.push(next_val);
2517 Ok(true)
2518 }
2519 None => {
2520 state.set_top_idx(top - 1);
2521 Ok(false)
2522 }
2523 }
2524}
2525
2526pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2532 let _level = index_to_stack_idx(state, idx);
2533 Ok(())
2534}
2535
2536pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2537 if n > 0 {
2538 state.concat(n)?;
2539 } else {
2540 let empty = state.intern_str(b"")?;
2541 state.push(LuaValue::Str(empty));
2542 }
2543 state.gc_pre_collect_clear();
2544 state.gc().check_step();
2545 Ok(())
2546}
2547
2548pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2549 let t = index_to_value(state, idx);
2550 let result = state.obj_len(&t)?;
2551 state.push(result);
2552 Ok(())
2553}
2554
2555pub fn set_warn_f(state: &mut LuaState, f: Option<Box<dyn FnMut(&[u8], bool)>>) {
2559 state.global_mut().warnf = f;
2561}
2562
2563pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2564 state.emit_warning(msg, tocont);
2565}
2566
2567pub fn new_userdata_uv(
2568 state: &mut LuaState,
2569 size: usize,
2570 nuvalue: i32,
2571) -> Result<GcRef<LuaUserData>, LuaError> {
2572 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2573 let u = state.new_userdata(size, nuvalue as usize)?;
2574 state.push(LuaValue::UserData(u.clone()));
2575 state.gc_pre_collect_clear();
2576 state.gc().check_step();
2577 Ok(u)
2578}
2579
2580fn aux_upvalue(state: &LuaState, fi: &LuaValue, n: i32) -> Option<(Vec<u8>, LuaValue)> {
2586 match fi {
2587 LuaValue::Function(LuaClosure::C(ccl)) => {
2588 let upvalues = ccl.upvalues.borrow();
2589 let nupvalues = upvalues.len() as i32;
2590 if n < 1 || n > nupvalues {
2591 return None;
2592 }
2593 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2594 }
2595 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2596 let nupvalues = lcl.upvals.len() as i32;
2597 if n < 1 || n > nupvalues {
2598 return None;
2599 }
2600 let val = state.upvalue_get(lcl, (n - 1) as usize);
2601 let no_name: &[u8] = if state.global().lua_version == lua_types::LuaVersion::V53 {
2605 b"(*no name)"
2606 } else {
2607 b"(no name)"
2608 };
2609 let name: Vec<u8> = lcl
2610 .proto
2611 .upvalues
2612 .get((n - 1) as usize)
2613 .and_then(|ud| ud.name.as_ref())
2614 .map(|s| s.as_bytes().to_vec())
2615 .unwrap_or_else(|| no_name.to_vec());
2616 Some((name, val))
2617 }
2618 _ => None,
2619 }
2620}
2621
2622pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2623 let fi = index_to_value(state, funcindex);
2624 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2625 state.push(val);
2626 Some(name)
2627 } else {
2628 None
2629 }
2630}
2631
2632pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2633 let fi = index_to_value(state, funcindex);
2634 let (name, _) = aux_upvalue(state, &fi, n)?;
2635 let new_val = state.pop();
2636 match &fi {
2637 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2638 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2639 }
2640 LuaValue::Function(LuaClosure::C(ccl)) => {
2641 let idx = (n - 1) as usize;
2642 {
2643 let mut upvalues = ccl.upvalues.borrow_mut();
2644 if idx >= upvalues.len() {
2645 return None;
2646 }
2647 upvalues[idx] = new_val.clone();
2648 }
2649 state.gc().barrier(ccl, &new_val);
2650 }
2651 _ => return None,
2652 }
2653 Some(name)
2654}
2655
2656fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2659 let fi = index_to_value(state, fidx);
2660 debug_assert!(
2661 matches!(fi, LuaValue::Function(LuaClosure::Lua(_))),
2662 "Lua function expected"
2663 );
2664 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2665 let sizeupvalues = lcl.upvals.len() as i32;
2666 if n >= 1 && n <= sizeupvalues {
2667 Some((n - 1) as usize)
2668 } else {
2669 None
2670 }
2671 } else {
2672 None
2673 }
2674}
2675
2676pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2678 let fi = index_to_value(state, fidx);
2679 match &fi {
2680 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2681 let idx = get_upval_ref_idx(state, fidx, n)?;
2682 Some(GcRef::identity(&lcl.upval(idx)))
2684 }
2685 LuaValue::Function(LuaClosure::C(ccl)) => {
2686 let upvalues = ccl.upvalues.borrow();
2687 if n >= 1 && n <= upvalues.len() as i32 {
2688 Some(GcRef::identity(ccl) ^ (n as usize))
2692 } else {
2693 None
2694 }
2695 }
2696 LuaValue::Function(LuaClosure::LightC(_)) => None,
2697 _ => {
2698 debug_assert!(false, "function expected");
2699 None
2700 }
2701 }
2702}
2703
2704pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2706 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2707 Some(i) => i,
2708 None => return,
2709 };
2710 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2711 Some(i) => i,
2712 None => return,
2713 };
2714 let f1 = index_to_value(state, fidx1);
2715 let f2 = index_to_value(state, fidx2);
2716 if let (LuaValue::Function(LuaClosure::Lua(lcl1)), LuaValue::Function(LuaClosure::Lua(lcl2))) =
2717 (&f1, &f2)
2718 {
2719 let shared = lcl2.upval(idx2);
2720 lcl1.set_upval(idx1, shared);
2721 state.gc().obj_barrier(lcl1, &shared);
2722 }
2723}