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 let legacy_no_check = matches!(
451 self.global().lua_version,
452 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
453 );
454 if matches!(result, LuaValue::Int(_) | LuaValue::Float(_)) {
455 return Ok(crate::object::num_to_string(self, &result)?
456 .as_bytes()
457 .to_vec());
458 }
459 if legacy_no_check {
460 let display_idx = StackIdx(top.0 - 1).0 as i32;
461 return display_default_bytes(self, &result, display_idx);
462 }
463 return Err(crate::debug::c_api_runtime(
464 self,
465 b"'__tostring' must return a string".to_vec(),
466 ));
467 }
468 }
469 let bytes: Vec<u8> = match &v {
470 LuaValue::Str(s) => {
471 let out = s.as_bytes().to_vec();
472 self.push(LuaValue::Str(s.clone()));
473 out
474 }
475 LuaValue::Int(_) | LuaValue::Float(_) => {
476 let s = crate::object::num_to_string(self, &v)?;
477 let out = s.as_bytes().to_vec();
478 self.push(LuaValue::Str(s));
479 out
480 }
481 LuaValue::Bool(b) => {
482 let lit: &[u8] = if *b { b"true" } else { b"false" };
483 let s = self.intern_str(lit)?;
484 self.push(LuaValue::Str(s));
485 lit.to_vec()
486 }
487 LuaValue::Nil => {
488 let s = self.intern_str(b"nil")?;
489 self.push(LuaValue::Str(s));
490 b"nil".to_vec()
491 }
492 _ => {
493 let kind = crate::tagmethods::obj_type_name(self, &v)?;
494 let ptr = to_pointer(self, abs).unwrap_or(0);
495 let mut buf = kind;
496 buf.extend_from_slice(b": 0x");
497 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
498 let s = self.intern_str(&buf)?;
499 self.push(LuaValue::Str(s));
500 buf
501 }
502 };
503 Ok(bytes)
504 }
505
506 pub fn top(&mut self) -> i32 {
513 get_top(self)
514 }
515
516 pub fn get_top(&mut self) -> i32 {
520 get_top(self)
521 }
522
523 pub fn type_at(&mut self, idx: i32) -> LuaType {
528 lua_type_at(self, idx)
529 }
530
531 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
537 if lua_type_at(self, arg) == LuaType::None {
538 return Err(crate::debug::arg_error_impl(self, arg, b"value expected"));
539 }
540 Ok(())
541 }
542
543 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
553 match to_lua_string(self, arg)? {
554 Some(s) => Ok(s.as_bytes().to_vec()),
555 None => {
556 let got = index_to_value(self, arg);
557 let got_name = if lua_type_at(self, arg) == LuaType::None {
558 b"no value".to_vec()
559 } else {
560 crate::tagmethods::obj_type_name(self, &got)?
561 };
562 let extramsg = format!(
563 "string expected, got {}",
564 String::from_utf8_lossy(&got_name)
565 );
566 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
567 }
568 }
569 }
570
571 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
583 match to_integer_x(self, arg) {
584 Some(d) => Ok(d),
585 None => {
586 if is_number(self, arg) {
587 Err(crate::debug::arg_error_impl(
588 self,
589 arg,
590 b"number has no integer representation",
591 ))
592 } else {
593 let got = index_to_value(self, arg);
594 let got_name = if lua_type_at(self, arg) == LuaType::None {
595 b"no value".to_vec()
596 } else {
597 crate::tagmethods::obj_type_name(self, &got)?
598 };
599 let extramsg = format!(
600 "number expected, got {}",
601 String::from_utf8_lossy(&got_name)
602 );
603 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
604 }
605 }
606 }
607 }
608
609 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
619 match to_number_x(self, arg) {
620 Some(d) => Ok(d),
621 None => {
622 let got = index_to_value(self, arg);
623 let got_name = if lua_type_at(self, arg) == LuaType::None {
624 b"no value".to_vec()
625 } else {
626 crate::tagmethods::obj_type_name(self, &got)?
627 };
628 let extramsg = format!(
629 "number expected, got {}",
630 String::from_utf8_lossy(&got_name)
631 );
632 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
633 }
634 }
635 }
636
637 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
647 match lua_type_at(self, arg) {
648 LuaType::None | LuaType::Nil => Ok(def),
649 _ => match to_integer_x(self, arg) {
650 Some(d) => Ok(d),
651 None => {
652 if is_number(self, arg) {
653 Err(LuaError::arg_error(
654 arg,
655 "number has no integer representation",
656 ))
657 } else {
658 let got = index_to_value(self, arg);
659 Err(LuaError::type_arg_error(arg, "number", &got))
660 }
661 }
662 },
663 }
664 }
665
666 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
673 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
674 }
675
676 pub fn protected_call_k(
680 &mut self,
681 nargs: i32,
682 nresults: i32,
683 msgh: i32,
684 ctx: isize,
685 k: Option<crate::state::LuaKFunction>,
686 ) -> Result<(), LuaError> {
687 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
688 }
689
690 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
691 push_lstring(self, s)?;
692 Ok(())
693 }
694
695 pub fn push_c_closure(
696 &mut self,
697 f: fn(&mut LuaState) -> Result<usize, LuaError>,
698 n: i32,
699 ) -> Result<(), LuaError> {
700 push_cclosure(self, f, n)
701 }
702
703 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
704 raw_set_i(self, idx, n)
705 }
706
707 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
708 set_i(self, idx, n)
709 }
710
711 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
715 get_i_value(self, t, n)
716 }
717
718 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
722 set_i_value(self, t, n)
723 }
724
725 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
726 create_table(self, narr, nrec)
727 }
728
729 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
733 set_field(self, LUA_REGISTRYINDEX, key)
734 }
735
736 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
742 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
743 return Ok(false);
744 }
745 self.pop_n(1);
746 create_table(self, 0, 2)?;
747 push_lstring(self, tname)?;
748 set_field(self, -2, b"__name")?;
749 push_value(self, -1);
750 set_field(self, LUA_REGISTRYINDEX, tname)?;
751 Ok(true)
752 }
753
754 pub fn new_lib(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
761 create_table(self, 0, funcs.len() as i32)?;
762 for (name, f) in funcs {
763 push_cclosure(self, *f, 0)?;
764 set_field(self, -2, name)?;
765 }
766 Ok(())
767 }
768
769 pub fn register_lib(
775 &mut self,
776 _name: &[u8],
777 funcs: &[(&[u8], LuaCFunction)],
778 ) -> Result<(), LuaError> {
779 self.new_lib(funcs)
780 }
781
782 pub fn new_lib_table(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
791 create_table(self, 0, funcs.len() as i32)
792 }
793
794 pub fn set_funcs_with_upvalues(
799 &mut self,
800 funcs: &[(&[u8], LuaCFunction)],
801 nup: i32,
802 ) -> Result<(), LuaError> {
803 check_stack(self, nup);
804 for (name, f) in funcs {
805 for _ in 0..nup {
806 push_value(self, -nup);
807 }
808 push_cclosure(self, *f, nup)?;
809 set_field(self, -(nup + 2), name)?;
810 }
811 self.pop_n(nup as usize);
812 Ok(())
813 }
814
815 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
816 set_metatable(self, objindex)?;
817 Ok(())
818 }
819
820 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
826 get_field(self, LUA_REGISTRYINDEX, name)?;
827 set_metatable(self, -2)?;
828 Ok(())
829 }
830
831 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
835 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
836 return Ok(true);
837 }
838 self.pop_n(1);
839 let idx = abs_index(self, LUA_REGISTRYINDEX);
840 let new_tbl = self.new_table();
841 self.push(LuaValue::Table(new_tbl));
842 push_value(self, -1);
843 set_field(self, idx, name)?;
844 Ok(false)
845 }
846
847 pub fn new_userdata_typed(
857 &mut self,
858 _name: &[u8],
859 size: usize,
860 nuvalue: i32,
861 ) -> Result<GcRef<LuaUserData>, LuaError> {
862 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
863 let u = GcRef::new(LuaUserData {
865 data: vec![0u8; size].into_boxed_slice(),
866 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
867 metatable: std::cell::RefCell::new(None),
868 host_value: std::cell::RefCell::new(None),
869 });
870 u.account_buffer(u.buffer_bytes() as isize);
871 self.push(LuaValue::UserData(u.clone()));
872 self.gc_pre_collect_clear();
873 self.gc().check_step();
874 Ok(u)
875 }
876}
877
878fn value_pointer(o: &LuaValue) -> Option<usize> {
882 match o {
883 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
884 LuaValue::LightUserData(p) => Some(*p as usize),
885 LuaValue::Str(s) => Some(GcRef::identity(s)),
886 LuaValue::Table(t) => Some(GcRef::identity(t)),
887 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
888 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
889 LuaValue::UserData(u) => Some(GcRef::identity(u)),
890 LuaValue::Thread(t) => Some(GcRef::identity(t)),
891 _ => None,
892 }
893}
894
895fn display_default_bytes(
901 state: &mut LuaState,
902 val: &LuaValue,
903 _idx: i32,
904) -> Result<Vec<u8>, LuaError> {
905 match val {
906 LuaValue::Str(s) => Ok(s.as_bytes().to_vec()),
907 LuaValue::Int(_) | LuaValue::Float(_) => {
908 Ok(crate::object::num_to_string(state, val)?.as_bytes().to_vec())
909 }
910 LuaValue::Bool(b) => Ok(if *b { b"true".to_vec() } else { b"false".to_vec() }),
911 LuaValue::Nil => Ok(b"nil".to_vec()),
912 _ => {
913 let kind = crate::tagmethods::obj_type_name(state, val)?;
914 let ptr = value_pointer(val).unwrap_or(0);
915 let mut buf = kind;
916 buf.extend_from_slice(b": 0x");
917 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
918 Ok(buf)
919 }
920 }
921}
922
923pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
926 if !is_valid_index(state, idx) {
927 return LuaType::None;
928 }
929 index_to_value(state, idx).base_type()
930}
931
932pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
933 t.type_name()
934}
935
936pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
937 let o = index_to_value(state, idx);
938 matches!(
939 o,
940 LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_))
941 )
942}
943
944pub fn is_integer(state: &LuaState, idx: i32) -> bool {
945 let o = index_to_value(state, idx);
946 matches!(o, LuaValue::Int(_))
947}
948
949pub fn is_number(state: &LuaState, idx: i32) -> bool {
950 let o = index_to_value(state, idx);
951 o.to_number_with_strconv().is_some()
952}
953
954pub fn is_string(state: &LuaState, idx: i32) -> bool {
955 let o = index_to_value(state, idx);
956 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
957}
958
959pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
960 let o = index_to_value(state, idx);
961 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
962}
963
964pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
965 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
966 return false;
967 }
968 let o1 = index_to_value(state, index1);
969 let o2 = index_to_value(state, index2);
970 state.equal_obj(None, &o1, &o2)
971}
972
973pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
975 const LUA_OPUNM: i32 = 12;
978 const LUA_OPBNOT: i32 = 14;
979 if op == LUA_OPUNM || op == LUA_OPBNOT {
980 let top_val = state.get_at(state.top_idx() - 1);
982 state.push(top_val);
983 }
984 let top = state.top_idx();
985 let a = state.get_at(top - 2);
986 let b = state.get_at(top - 1);
987 let result = state.arith_op(op, &a, &b)?;
988 state.set_at(top - 2, result);
989 state.pop();
990 Ok(())
991}
992
993pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
994 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
995 let o1 = index_to_value(state, index1);
996 let o2 = index_to_value(state, index2);
997 if valid {
998 match op {
999 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
1000 1 => state.less_than(&o1, &o2),
1001 2 => state.less_equal(&o1, &o2),
1002 _ => {
1003 debug_assert!(false, "invalid option");
1004 Ok(false)
1005 }
1006 }
1007 } else {
1008 Ok(false)
1009 }
1010}
1011
1012pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
1013 match state.str_to_num(s) {
1015 Some((val, consumed)) => {
1016 state.push(val);
1017 consumed
1018 }
1019 None => 0,
1020 }
1021}
1022
1023pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
1024 let o = index_to_value(state, idx);
1025 o.to_number_with_strconv()
1026}
1027
1028pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
1029 let o = index_to_value(state, idx);
1030 o.to_integer_with_strconv()
1031}
1032
1033pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
1034 let o = index_to_value(state, idx);
1035 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
1036}
1037
1038pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Option<GcRef<LuaString>>, LuaError> {
1040 let o = index_to_value(state, idx);
1041 if let LuaValue::Str(s) = &o {
1042 return Ok(Some(s.clone()));
1043 }
1044 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
1045 return Ok(None);
1046 }
1047 state.obj_to_string(idx)?;
1048 state.gc_pre_collect_clear();
1049 state.gc().check_step();
1050 let updated = index_to_value(state, idx);
1051 if let LuaValue::Str(s) = updated {
1052 Ok(Some(s))
1053 } else {
1054 Ok(None)
1055 }
1056}
1057
1058pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
1059 let o = index_to_value(state, idx);
1060 match &o {
1061 LuaValue::Str(s) => s.len() as u64,
1062 LuaValue::UserData(u) => u.len() as u64,
1063 LuaValue::Table(t) => state.table_getn(t) as u64,
1064 _ => 0,
1065 }
1066}
1067
1068pub fn to_cfunction(
1069 state: &LuaState,
1070 idx: i32,
1071) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
1072 let o = index_to_value(state, idx);
1073 match o {
1074 LuaValue::Function(LuaClosure::LightC(_f)) => None,
1078 LuaValue::Function(LuaClosure::C(_ccl)) => None,
1079 _ => None,
1080 }
1081}
1082
1083#[inline]
1084fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1085 match o {
1086 LuaValue::UserData(u) => {
1087 let _ = u;
1091 None
1092 }
1093 LuaValue::LightUserData(p) => Some(*p),
1094 _ => None,
1095 }
1096}
1097
1098pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1099 let o = index_to_value(state, idx);
1100 to_userdata_ptr(&o)
1101}
1102
1103pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1104 let o = index_to_value(state, idx);
1108 if let LuaValue::Thread(t) = o {
1109 Some(t)
1110 } else {
1111 None
1112 }
1113}
1114
1115pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1118 let o = index_to_value(state, idx);
1119 match &o {
1122 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1123 LuaValue::LightUserData(p) => Some(*p as usize),
1124 LuaValue::Str(s) => Some(GcRef::identity(s)),
1125 LuaValue::Table(t) => Some(GcRef::identity(t)),
1126 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1127 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1128 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1129 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1130 _ => None,
1131 }
1132}
1133
1134pub fn push_nil(state: &mut LuaState) {
1137 state.push(LuaValue::Nil);
1138}
1139
1140pub fn push_number(state: &mut LuaState, n: f64) {
1141 state.push(LuaValue::Float(n));
1142}
1143
1144pub fn push_integer(state: &mut LuaState, n: i64) {
1145 state.push(LuaValue::Int(n));
1146}
1147
1148pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1150 let ts = state.intern_str(s)?;
1151 state.push(LuaValue::Str(ts.clone()));
1152 state.gc_pre_collect_clear();
1153 state.gc().check_step();
1154 Ok(ts)
1155}
1156
1157pub fn push_string(
1158 state: &mut LuaState,
1159 s: Option<&[u8]>,
1160) -> Result<Option<GcRef<LuaString>>, LuaError> {
1161 match s {
1162 None => {
1163 state.push(LuaValue::Nil);
1164 state.gc_pre_collect_clear();
1165 state.gc().check_step();
1166 Ok(None)
1167 }
1168 Some(bytes) => {
1169 let ts = state.intern_str(bytes)?;
1170 state.push(LuaValue::Str(ts.clone()));
1171 state.gc_pre_collect_clear();
1172 state.gc().check_step();
1173 Ok(Some(ts))
1174 }
1175 }
1176}
1177
1178pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1182 let ts = state.intern_str(formatted)?;
1183 state.push(LuaValue::Str(ts.clone()));
1184 state.gc_pre_collect_clear();
1185 state.gc().check_step();
1186 Ok(ts)
1187}
1188
1189pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1191 push_vfstring(state, formatted)
1192}
1193
1194pub fn push_cclosure(
1195 state: &mut LuaState,
1196 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1197 n: i32,
1198) -> Result<(), LuaError> {
1199 let idx: lua_types::closure::LuaCFnPtr = {
1213 let mut g = state.global_mut();
1214 if n == 0 {
1215 match g.c_functions.iter().position(|existing| {
1216 existing
1217 .as_bare()
1218 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1219 }) {
1220 Some(i) => i,
1221 None => {
1222 let i = g.c_functions.len();
1223 g.c_functions.push(LuaCallable::bare(f));
1224 i
1225 }
1226 }
1227 } else {
1228 let i = g.c_functions.len();
1229 g.c_functions.push(LuaCallable::bare(f));
1230 i
1231 }
1232 };
1233 if n == 0 {
1234 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1235 } else {
1236 debug_assert!(
1237 n > 0 && (n as u32) <= MAX_UPVAL as u32,
1238 "upvalue index too large"
1239 );
1240 let n_usize = n as usize;
1241 let top = state.top_idx();
1242 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1243 let base = top.0 as usize - n_usize;
1244 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1245 for i in 0..n_usize {
1246 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1247 }
1248 state.pop_n(n_usize);
1249 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1251 func: idx,
1252 upvalues: std::cell::RefCell::new(upvalues),
1253 }));
1254 if let LuaClosure::C(ccl) = &cl {
1255 ccl.account_buffer(ccl.buffer_bytes() as isize);
1256 }
1257 state.push(LuaValue::Function(cl));
1258 state.gc_pre_collect_clear();
1259 state.gc().check_step();
1260 }
1261 Ok(())
1262}
1263
1264pub fn push_boolean(state: &mut LuaState, b: bool) {
1265 state.push(LuaValue::Bool(b));
1266}
1267
1268pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1269 state.push(LuaValue::LightUserData(p));
1270}
1271
1272pub fn push_thread(state: &mut LuaState) -> bool {
1274 let (value, is_main) = {
1275 let g = state.global();
1276 let id = g.current_thread_id;
1277 let v = g
1278 .thread_value_for(id)
1279 .expect("current_thread_id must always resolve to a registered thread");
1280 (v, id == g.main_thread_id)
1281 };
1282 state.push(LuaValue::Thread(value));
1283 is_main
1284}
1285
1286fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1289 let str_val = {
1290 let ts = state.intern_str(k)?;
1291 LuaValue::Str(ts)
1292 };
1293 let result = state.table_get_with_tm(&t, &str_val)?;
1296 state.push(result);
1297 let top = state.top_idx();
1298 Ok(state.get_at(top - 1).base_type())
1299}
1300
1301fn get_global_table(state: &LuaState) -> LuaValue {
1302 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1315 let running = state.global().current_thread_id;
1316 return state.v51_thread_lgt(running);
1317 }
1318 state.global().globals.clone()
1319}
1320
1321pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1322 let g = get_global_table(state);
1323 aux_get_str(state, g, name)
1324}
1325
1326pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1327 let t = index_to_value(state, idx);
1328 let top = state.top_idx();
1329 let key = state.get_at(top - 1);
1330 let result = state.table_get_with_tm(&t, &key)?;
1331 state.set_at(top - 1, result);
1332 let val = state.get_at(top - 1);
1333 Ok(val.base_type())
1334}
1335
1336pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1337 let t = index_to_value(state, idx);
1338 aux_get_str(state, t, k)
1339}
1340
1341pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1342 let t = index_to_value(state, idx);
1343 let key = LuaValue::Int(n);
1344 let result = state.table_get_with_tm(&t, &key)?;
1345 state.push(result);
1346 let top = state.top_idx();
1347 Ok(state.get_at(top - 1).base_type())
1348}
1349
1350pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1356 let key = LuaValue::Int(n);
1357 let result = state.table_get_with_tm(t, &key)?;
1358 state.push(result);
1359 let top = state.top_idx();
1360 Ok(state.get_at(top - 1).base_type())
1361}
1362
1363fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1364 let v = val.unwrap_or(LuaValue::Nil);
1365 state.push(v);
1366 let top = state.top_idx();
1367 state.get_at(top - 1).base_type()
1368}
1369
1370fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1371 let t = index_to_value(state, idx);
1372 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1373 if let LuaValue::Table(tbl) = t {
1374 Some(tbl)
1375 } else {
1376 None
1377 }
1378}
1379
1380pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1381 let t = get_table_value(state, idx);
1382 let top = state.top_idx();
1383 let key = state.get_at(top - 1);
1384 let val = t.as_ref().map(|tbl| tbl.get(&key));
1385 state.set_top_idx(top - 1);
1386 finish_raw_get(state, val)
1387}
1388
1389pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1390 let t = get_table_value(state, idx);
1391 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1392 finish_raw_get(state, val)
1393}
1394
1395pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1396 let t = get_table_value(state, idx);
1397 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1398 let val = t.as_ref().map(|tbl| tbl.get(&key));
1399 finish_raw_get(state, val)
1400}
1401
1402pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1403 let t = state.new_table();
1404 if narray > 0 || nrec > 0 {
1405 t.resize(state, narray as usize, nrec as usize)?;
1406 }
1407 state.push(LuaValue::Table(t));
1408 state.gc_pre_collect_clear();
1409 state.gc().check_step();
1410 Ok(())
1411}
1412
1413pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1414 let obj = index_to_value(state, objindex);
1415 let mt: Option<GcRef<LuaTable>> = match &obj {
1416 LuaValue::Table(t) => t.metatable(),
1417 LuaValue::UserData(u) => u.metatable(),
1418 other => {
1419 let idx = other.base_type() as usize;
1420 state.global().mt[idx].clone()
1421 }
1422 };
1423 if let Some(mt_table) = mt {
1424 state.push(LuaValue::Table(mt_table));
1425 true
1426 } else {
1427 false
1428 }
1429}
1430
1431pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1432 let o = index_to_value(state, idx);
1433 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1434 if let LuaValue::UserData(ref u) = o {
1435 let uv = u.uv.borrow();
1436 let uv_count = uv.len() as i32;
1437 if n <= 0 || n > uv_count {
1438 state.push(LuaValue::Nil);
1439 LuaType::None
1440 } else {
1441 let val = uv[(n - 1) as usize].clone();
1442 let t = val.base_type();
1443 state.push(val);
1444 t
1445 }
1446 } else {
1447 state.push(LuaValue::Nil);
1448 LuaType::None
1449 }
1450}
1451
1452fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1455 let str_val = {
1456 let ts = state.intern_str(k)?;
1457 LuaValue::Str(ts)
1458 };
1459 let top = state.top_idx();
1464 let val = state.get_at(top - 1);
1465 state.table_set_with_tm(&t, str_val, val)?;
1466 state.pop();
1467 Ok(())
1468}
1469
1470pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1471 let g = get_global_table(state);
1472 aux_set_str(state, g, name)
1473}
1474
1475pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1476 let t = index_to_value(state, idx);
1477 let top = state.top_idx();
1478 let key = state.get_at(top - 2);
1479 let val = state.get_at(top - 1);
1480 state.table_set_with_tm(&t, key, val)?;
1481 state.set_top_idx(top - 2);
1482 Ok(())
1483}
1484
1485pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1486 let t = index_to_value(state, idx);
1487 aux_set_str(state, t, k)
1488}
1489
1490pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1491 let t = index_to_value(state, idx);
1492 let top = state.top_idx();
1493 let val = state.get_at(top - 1);
1494 let key = LuaValue::Int(n);
1495 state.table_set_with_tm(&t, key, val)?;
1496 state.pop();
1497 Ok(())
1498}
1499
1500pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1506 let top = state.top_idx();
1507 let val = state.get_at(top - 1);
1508 let key = LuaValue::Int(n);
1509 state.table_set_with_tm(t, key, val)?;
1510 state.pop();
1511 Ok(())
1512}
1513
1514fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1515 let t = get_table_value(state, idx)
1516 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1517 let top = state.top_idx();
1518 let val = state.get_at(top - 1);
1519 t.raw_set(state, key, val)?;
1520 t.invalidate_tm_cache();
1521 let top_val = state.get_at(top - 1);
1522 state.gc().table_barrier_back(&t, &top_val);
1523 state.set_top_idx(top - n as i32);
1524 Ok(())
1525}
1526
1527pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1528 let top = state.top_idx();
1529 let key = state.get_at(top - 2);
1530 aux_raw_set(state, idx, key, 2)
1531}
1532
1533pub fn raw_set_p(
1534 state: &mut LuaState,
1535 idx: i32,
1536 p: *const core::ffi::c_void,
1537) -> Result<(), LuaError> {
1538 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1539 aux_raw_set(state, idx, key, 1)
1540}
1541
1542pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1543 let t = get_table_value(state, idx)
1544 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1545 let top = state.top_idx();
1546 let val = state.get_at(top - 1);
1547 t.raw_set_int(state, n, val)?;
1548 let top_val = state.get_at(top - 1);
1549 state.gc().table_barrier_back(&t, &top_val);
1550 state.pop();
1551 Ok(())
1552}
1553
1554fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1559 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1560 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1561}
1562
1563fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1564 let heap_ptr = object.heap_ptr();
1565 let mut g = state.global_mut();
1566 if g.finalizers.push_pending_unique(object) {
1567 if let Some(ptr) = heap_ptr {
1568 g.heap.move_allgc_to_finobj(ptr);
1569 }
1570 }
1571}
1572
1573struct Udata51RosterEntry {
1588 weak: lua_types::gc::GcWeak<LuaUserData>,
1589 submitted: std::cell::Cell<bool>,
1590}
1591
1592impl lua_gc::Udata51Probe for Udata51RosterEntry {
1593 fn is_alive(&self) -> bool {
1594 self.weak.upgrade().is_some()
1595 }
1596
1597 fn as_any(&self) -> &dyn std::any::Any {
1598 self
1599 }
1600}
1601
1602fn roster_v51_userdata(state: &LuaState, ud: &GcRef<LuaUserData>) {
1612 if !matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1613 return;
1614 }
1615 let probe = std::rc::Rc::new(Udata51RosterEntry {
1616 weak: ud.downgrade(),
1617 submitted: std::cell::Cell::new(false),
1618 });
1619 state.global().heap.register_v51_udata(probe);
1620}
1621
1622pub fn scan_v51_finalizable_userdata(state: &mut LuaState) {
1634 if !matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1635 return;
1636 }
1637 let probes = state.global().heap.scan_v51_finalizable();
1638 if probes.is_empty() {
1639 return;
1640 }
1641 let gc_name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1642 for probe in &probes {
1643 let entry = match probe.as_any().downcast_ref::<Udata51RosterEntry>() {
1644 Some(entry) => entry,
1645 None => continue,
1646 };
1647 if entry.submitted.get() {
1648 continue;
1649 }
1650 let ud = match entry.weak.upgrade() {
1651 Some(ud) => ud,
1652 None => continue,
1653 };
1654 let has_gc = match ud.metatable() {
1655 Some(mt) => !matches!(mt.get_short_str(&gc_name), LuaValue::Nil),
1656 None => false,
1657 };
1658 if has_gc {
1659 entry.submitted.set(true);
1660 register_finalizable_object(state, FinalizerObject::UserData(ud));
1661 }
1662 }
1663}
1664
1665pub fn run_pending_finalizers(state: &mut LuaState) {
1668 let _ = run_pending_finalizers_inner(state, false);
1669}
1670
1671const GC_FIN_MAX: usize = 10;
1672
1673pub fn run_pending_finalizers_inner(state: &mut LuaState, propagate: bool) -> Result<(), LuaError> {
1693 run_pending_finalizers_limited(state, propagate, None).map(|_| ())
1694}
1695
1696pub(crate) fn run_some_pending_finalizers_inner(
1697 state: &mut LuaState,
1698 propagate: bool,
1699) -> Result<usize, LuaError> {
1700 run_pending_finalizers_limited(state, propagate, Some(GC_FIN_MAX))
1701}
1702
1703fn run_pending_finalizers_limited(
1704 state: &mut LuaState,
1705 propagate: bool,
1706 limit: Option<usize>,
1707) -> Result<usize, LuaError> {
1708 let version = state.global().lua_version;
1709 let mut processed = 0usize;
1710 loop {
1711 if limit.map_or(false, |limit| processed >= limit) {
1712 break;
1713 }
1714 if !state.global().finalizers.has_to_be_finalized() {
1718 break;
1719 }
1720 let object = {
1730 let mut g = state.global_mut();
1731 let object = g
1732 .finalizers
1733 .pop_to_be_finalized()
1734 .expect("to-be-finalized checked non-empty");
1735 if let Some(ptr) = object.heap_ptr() {
1736 g.heap.move_tobefnz_to_allgc(ptr);
1737 }
1738 object
1739 };
1740 processed += 1;
1741 let mt = object.metatable();
1742 let gc_fn = match mt {
1743 Some(ref m) => {
1744 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1745 m.get_short_str(&name)
1746 }
1747 None => LuaValue::Nil,
1748 };
1749 if !matches!(gc_fn, LuaValue::Function(_)) {
1750 continue;
1751 }
1752 let saved_top = state.top_idx();
1753 let ci_top = state.current_call_info().top;
1754 if saved_top.0 < ci_top.0 {
1755 state.clear_stack_range(saved_top, ci_top);
1756 state.set_top(ci_top);
1757 }
1758 state.push(gc_fn);
1759 state.push(object.as_lua_value());
1760 let func_idx = state.top_idx() - 2;
1761 let _heap_guard = {
1762 let g = state.global.borrow();
1763 lua_gc::HeapGuard::push(&g.heap)
1764 };
1765 let old_allowhook = state.allowhook;
1766 let old_gcstp = state.global_mut().stop_gc_internal();
1767 state.allowhook = false;
1768 let caller_ci = state.ci;
1769 let caller_status = state.get_ci(caller_ci).callstatus;
1770 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1771 let status = crate::do_::pcall(state, |s| s.call_no_yield(func_idx, 0), func_idx, 0);
1772 let finalizer_error: Option<LuaValue> = if status != LuaStatus::Ok {
1776 Some(state.get_at(state.top_idx() - 1).clone())
1777 } else {
1778 None
1779 };
1780 state.get_ci_mut(caller_ci).callstatus = caller_status;
1781 state.allowhook = old_allowhook;
1782 state.global_mut().set_gc_stop_flags(old_gcstp);
1783 state.set_top(saved_top);
1784
1785 if let Some(errobj) = finalizer_error {
1786 match version {
1787 lua_types::LuaVersion::V51 if propagate => {
1788 return Err(LuaError::from_value(errobj));
1795 }
1796 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1797 let msg: Vec<u8> = match &errobj {
1804 LuaValue::Str(s) => s.as_bytes().to_vec(),
1805 _ => b"no message".to_vec(),
1806 };
1807 let mut wrapped = b"error in __gc metamethod (".to_vec();
1808 wrapped.extend_from_slice(&msg);
1809 wrapped.push(b')');
1810 let interned = state.intern_str(&wrapped)?;
1811 return Err(LuaError::from_value(LuaValue::Str(interned)));
1812 }
1813 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1814 let msg: Vec<u8> = match &errobj {
1818 LuaValue::Str(s) => s.as_bytes().to_vec(),
1819 _ => b"error object is not a string".to_vec(),
1820 };
1821 state.emit_warning(b"error in ", true);
1822 state.emit_warning(b"__gc", true);
1823 state.emit_warning(b" (", true);
1824 state.emit_warning(&msg, true);
1825 state.emit_warning(b")", false);
1826 }
1827 _ => {}
1828 }
1829 }
1830 }
1831 Ok(processed)
1835}
1836
1837pub fn run_close_finalizers(state: &mut LuaState) {
1856 let pending: Vec<FinalizerObject> = state.global_mut().finalizers.take_pending();
1857 if pending.is_empty() {
1858 return;
1859 }
1860 let mut seen = std::collections::HashSet::<usize>::new();
1861 {
1862 let mut g = state.global_mut();
1863 for object in pending.into_iter().rev() {
1864 let heap_ptr = object.heap_ptr();
1865 if seen.insert(object.identity()) {
1866 g.finalizers.push_to_be_finalized(object);
1867 if let Some(ptr) = heap_ptr {
1868 g.heap.move_finobj_to_tobefnz(ptr);
1869 }
1870 }
1871 }
1872 }
1873 run_pending_finalizers(state);
1874}
1875
1876fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1882 let mut g = state.global_mut();
1883 g.weak_tables_registry.live_snapshot()
1884}
1885
1886pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1887 let top = state.top_idx();
1888 let mt_val = state.get_at(top - 1);
1889 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1890 None
1891 } else {
1892 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1893 if let LuaValue::Table(t) = mt_val {
1894 Some(t)
1895 } else {
1896 None
1897 }
1898 };
1899
1900 let obj = index_to_value(state, objindex);
1901 match obj {
1902 LuaValue::Table(ref tbl) => {
1903 if mt.is_some() {
1904 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1905 }
1906 tbl.set_metatable(mt.clone());
1907 if tbl.weak_mode() == 0 {
1908 state
1909 .global_mut()
1910 .weak_tables_registry
1911 .remove_identity(tbl.identity());
1912 } else {
1913 state
1914 .global_mut()
1915 .weak_tables_registry
1916 .push_unique(WeakTableEntry::new(tbl));
1917 }
1918 let tables_finalizable =
1928 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1929 if tables_finalizable {
1930 if let Some(ref mt_table) = mt {
1931 if metatable_has_gc(state, mt_table) {
1932 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1933 }
1934 }
1935 }
1936 }
1937 LuaValue::UserData(ref ud) => {
1938 if let Some(ref mt_table) = mt {
1939 state.gc().obj_barrier(ud, mt_table);
1940 if metatable_has_gc(state, mt_table) {
1941 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1942 }
1943 roster_v51_userdata(state, ud);
1948 }
1949 ud.set_metatable(mt);
1950 }
1951 ref other => {
1952 let idx = other.base_type() as usize;
1953 state.global_mut().mt[idx] = mt;
1954 }
1955 }
1956 state.pop();
1957 Ok(true)
1958}
1959
1960pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1961 let o = index_to_value(state, idx);
1962 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1963 let top = state.top_idx();
1964 let val = state.get_at(top - 1);
1965 let res = if let LuaValue::UserData(ref ud) = o {
1966 let mut uv = ud.uv.borrow_mut();
1967 let nuvalue = uv.len() as i32;
1968 if n < 1 || n > nuvalue {
1969 false
1970 } else {
1971 uv[(n - 1) as usize] = val.clone();
1972 drop(uv);
1973 state.gc().barrier_back(ud, &val);
1974 true
1975 }
1976 } else {
1977 false
1978 };
1979 state.pop();
1980 Ok(res)
1981}
1982
1983pub fn call_k(
1987 state: &mut LuaState,
1988 nargs: i32,
1989 nresults: i32,
1990 ctx: isize,
1991 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1992) -> Result<(), LuaError> {
1993 let top = state.top_idx();
1994 let func_idx = top - (nargs + 1);
1995 if k.is_some() && state.is_yieldable() {
2001 let ci_idx = state.ci;
2002 {
2003 let ci = state.get_ci_mut(ci_idx);
2004 ci.set_u_c_k(k);
2005 ci.set_u_c_ctx(ctx);
2006 }
2007 state.call_at(func_idx, nresults)?;
2008 } else {
2009 state.call_no_yield(func_idx, nresults)?;
2010 }
2011 state.adjust_results(nresults);
2012 Ok(())
2013}
2014
2015pub fn pcall_k(
2017 state: &mut LuaState,
2018 nargs: i32,
2019 nresults: i32,
2020 errfunc: i32,
2021 ctx: isize,
2022 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
2023) -> Result<LuaStatus, LuaError> {
2024 let _heap_guard = {
2029 let g = state.global.borrow();
2030 lua_gc::HeapGuard::push(&g.heap)
2035 };
2036 let err_handler_idx: isize = if errfunc == 0 {
2037 0
2038 } else {
2039 let o = index_to_stack_idx(state, errfunc);
2040 debug_assert!(
2041 matches!(state.get_at(o), LuaValue::Function(_)),
2042 "error handler must be a function"
2043 );
2044 o.0 as isize
2045 };
2046 let top = state.top_idx();
2047 let func_idx = top - (nargs + 1);
2048 if k.is_none() || !state.is_yieldable() {
2049 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
2050 state.adjust_results(nresults);
2051 return Ok(LuaStatus::Ok);
2052 }
2053 let ci_idx = state.ci;
2059 let allow = state.allowhook;
2060 let saved_errfunc = state.errfunc;
2061 {
2062 let ci = state.get_ci_mut(ci_idx);
2063 ci.set_u_c_k(k);
2064 ci.set_u_c_ctx(ctx);
2065 ci.set_u2_funcidx(func_idx.0 as i32);
2066 ci.set_u_c_old_errfunc(saved_errfunc);
2067 ci.set_oah(allow);
2068 ci.callstatus |= crate::state::CIST_YPCALL;
2069 }
2070 state.errfunc = err_handler_idx;
2071 let call_result = crate::do_::call(state, func_idx, nresults);
2072 match call_result {
2073 Ok(()) => {
2074 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
2077 state.errfunc = saved_errfunc;
2078 state.adjust_results(nresults);
2079 Ok(LuaStatus::Ok)
2080 }
2081 Err(crate::state::LuaError::Yield) => {
2082 Err(crate::state::LuaError::Yield)
2086 }
2087 Err(e) => {
2088 Err(e)
2092 }
2093 }
2094}
2095
2096pub fn load(
2100 state: &mut LuaState,
2101 reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
2102 chunkname: Option<&[u8]>,
2103 mode: Option<&[u8]>,
2104) -> Result<LuaStatus, LuaError> {
2105 let name = chunkname.unwrap_or(b"?");
2106 let z = crate::zio::ZIO::new(reader);
2107 let status = state.protected_parser(z, name, mode);
2108 if status == LuaStatus::Ok {
2109 let top = state.top_idx();
2110 let func_val = state.get_at(top - 1);
2111 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
2112 let inject_env = if state.global().lua_version == lua_types::LuaVersion::V52 {
2113 lcl.upvals.len() == 1
2114 } else {
2115 !lcl.upvals.is_empty()
2116 };
2117 if inject_env {
2118 let gt = get_global_table(state);
2119 let uv = state.new_upval_closed(gt);
2120 lcl.set_upval(0, uv);
2121 state.gc().obj_barrier(&lcl, &uv);
2122 }
2123 }
2124 }
2125 Ok(status)
2126}
2127
2128pub fn dump(
2129 state: &LuaState,
2130 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
2131 strip: bool,
2132) -> Result<bool, LuaError> {
2133 let top = state.top_idx();
2134 let o = state.get_at(top - 1);
2135 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
2136 crate::dump::dump(state, &lcl.proto, writer, strip)?;
2137 Ok(true)
2138 } else {
2139 Ok(false)
2140 }
2141}
2142
2143pub fn status(state: &LuaState) -> LuaStatus {
2144 LuaStatus::from_raw(state.status as i32)
2145}
2146
2147#[repr(i32)]
2151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2152pub enum GcWhat {
2153 Stop = 0,
2154 Restart = 1,
2155 Collect = 2,
2156 Count = 3,
2157 CountB = 4,
2158 Step = 5,
2159 SetPause = 6,
2160 SetStepMul = 7,
2161 IsRunning = 9,
2162 Gen = 10,
2163 Inc = 11,
2164}
2165
2166pub enum GcArgs {
2168 Stop,
2169 Restart,
2170 Collect,
2171 Count,
2172 CountB,
2173 Step {
2174 data: i32,
2175 },
2176 SetPause {
2177 value: i32,
2178 },
2179 SetStepMul {
2180 value: i32,
2181 },
2182 IsRunning,
2183 Gen {
2184 minormul: i32,
2185 majormul: i32,
2186 },
2187 Inc {
2188 pause: i32,
2189 stepmul: i32,
2190 stepsize: i32,
2191 },
2192 Param {
2195 param: usize,
2196 value: i64,
2197 },
2198}
2199
2200pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
2201 if let GcArgs::Param { param, value } = &args {
2205 return state.global_mut().gc55_param(*param, *value) as i32;
2206 }
2207 if state.global().is_gc_stopped_internally() {
2208 return -1;
2209 }
2210 match args {
2211 GcArgs::Stop => {
2212 state.global_mut().set_gc_stop_user();
2213 }
2214 GcArgs::Restart => {
2215 {
2216 let mut g = state.global_mut();
2217 crate::state::set_debt(&mut *g, 0);
2218 }
2219 state.global_mut().clear_gc_stop();
2220 }
2221 GcArgs::Collect => {
2222 if !state.allowhook {
2223 return 0;
2224 }
2225 scan_v51_finalizable_userdata(state);
2229 state.gc().full_collect();
2235 if let Err(e) = run_pending_finalizers_inner(state, true) {
2243 state.global_mut().gc_finalizer_error = Some(e.into_value());
2244 }
2245 }
2246 GcArgs::Count => {
2247 let g = state.global();
2248 let total = g.heap.bytes_used();
2249 return (total >> 10) as i32;
2250 }
2251 GcArgs::CountB => {
2252 let g = state.global();
2253 let total = g.heap.bytes_used();
2254 return (total & 0x3ff) as i32;
2255 }
2256 GcArgs::Step { data } => {
2257 let old_stp = {
2258 let mut g = state.global_mut();
2259 let old = g.gc_stop_flags();
2260 g.clear_gc_stop();
2261 old
2262 };
2263 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2270 let work_units = if data == 0 {
2271 stepmul
2272 } else {
2273 let raw = (data as isize).saturating_mul(stepmul);
2274 raw.max(1)
2275 };
2276 let debt_for_result = if data == 0 {
2277 let mut g = state.global_mut();
2278 crate::state::set_debt(&mut *g, 0);
2279 0
2280 } else {
2281 let debt = data as isize * 1024 + state.global().gc_debt();
2282 let mut g = state.global_mut();
2283 crate::state::set_debt(&mut *g, debt);
2284 debt
2285 };
2286 let gen_mode = state.global().is_gen_mode();
2287 let cycle_complete = if gen_mode {
2288 if data == 0 {
2289 state.gc().generational_step_minor_only();
2290 } else {
2291 state.gc().generational_step();
2292 }
2293 if state.global().finalizers.has_to_be_finalized() {
2294 if let Err(e) = run_pending_finalizers_inner(state, true) {
2295 state.global_mut().gc_finalizer_error = Some(e.into_value());
2296 }
2297 }
2298 debt_for_result > 0 && state.global().gc_at_pause()
2299 } else if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2300 && state.global().finalizers.has_to_be_finalized()
2301 {
2302 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2303 state.global_mut().gc_finalizer_error = Some(e.into_value());
2304 }
2305 if state.global().finalizers.has_to_be_finalized() {
2306 false
2307 } else {
2308 state.global().heap.finish_callfin_phase()
2309 }
2310 } else {
2311 let completed = state.gc().incremental_step(work_units);
2312 if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2313 && state.global().finalizers.has_to_be_finalized()
2314 {
2315 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2316 state.global_mut().gc_finalizer_error = Some(e.into_value());
2317 }
2318 if state.global().finalizers.has_to_be_finalized() {
2319 false
2320 } else {
2321 state.global().heap.finish_callfin_phase()
2322 }
2323 } else {
2324 completed
2325 }
2326 };
2327 state.global_mut().set_gc_stop_flags(old_stp);
2328 {
2330 let heap_state = state.global().heap.gc_state();
2331 let mut g = state.global_mut();
2332 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2333 }
2334 return if cycle_complete { 1 } else { 0 };
2335 }
2336 GcArgs::SetPause { value } => {
2337 let old = state.global().gc_pause_param();
2338 state.global_mut().set_gc_pause_param(value);
2339 return old;
2340 }
2341 GcArgs::SetStepMul { value } => {
2342 let old = state.global().gc_stepmul_param();
2343 state.global_mut().set_gc_stepmul_param(value);
2344 return old;
2345 }
2346 GcArgs::IsRunning => {
2347 return state.global().gc_running() as i32;
2348 }
2349 GcArgs::Gen { minormul, majormul } => {
2350 let old_mode = if state.global().is_gen_mode() {
2351 10i32
2352 } else {
2353 11i32
2354 };
2355 if minormul != 0 {
2356 state.global_mut().genminormul = minormul as u8;
2357 }
2358 if majormul != 0 {
2359 state.global_mut().set_gc_genmajormul(majormul);
2360 }
2361 state.gc().change_mode(crate::state::GcKind::Generational);
2362 return old_mode;
2363 }
2364 GcArgs::Inc {
2365 pause,
2366 stepmul,
2367 stepsize,
2368 } => {
2369 let old_mode = if state.global().is_gen_mode() {
2370 10i32
2371 } else {
2372 11i32
2373 };
2374 if pause != 0 {
2375 state.global_mut().set_gc_pause_param(pause);
2376 }
2377 if stepmul != 0 {
2378 state.global_mut().set_gc_stepmul_param(stepmul);
2379 }
2380 if stepsize != 0 {
2381 state.global_mut().gcstepsize = stepsize as u8;
2382 }
2383 state.gc().change_mode(crate::state::GcKind::Incremental);
2384 return old_mode;
2385 }
2386 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2387 }
2388 0
2389}
2390
2391pub fn configure_startup_gc_mode(state: &mut LuaState) {
2398 if matches!(
2399 state.global().lua_version,
2400 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2401 ) {
2402 let _ = gc(state, GcArgs::Restart);
2403 let _ = gc(
2404 state,
2405 GcArgs::Gen {
2406 minormul: 0,
2407 majormul: 0,
2408 },
2409 );
2410 }
2411}
2412
2413pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2420 let top = state.top_idx();
2424 let errobj = state.get_at(top - 1);
2425 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2426 let memerr = state.global().memerrmsg.clone();
2427 GcRef::ptr_eq(s, &memerr)
2428 } else {
2429 false
2430 };
2431 if is_mem_err {
2432 Err(LuaError::Memory)
2433 } else {
2434 Err(LuaError::from_value(errobj))
2435 }
2436}
2437
2438fn normalize_float_only_next_key(state: &LuaState, key: LuaValue) -> LuaValue {
2448 if state.global().lua_version.number_model() != lua_types::NumberModel::FloatOnly {
2449 return key;
2450 }
2451 if let LuaValue::Float(f) = key {
2452 let k = f as i64;
2453 if k as f64 == f {
2454 return LuaValue::Int(k);
2455 }
2456 }
2457 key
2458}
2459
2460pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2461 let t = get_table_value(state, idx)
2462 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2463 let top = state.top_idx();
2464 let key = normalize_float_only_next_key(state, state.get_at(top - 1));
2465 match t.next(key)? {
2466 Some((next_key, next_val)) => {
2467 state.set_at(top - 1, next_key);
2468 state.push(next_val);
2469 Ok(true)
2470 }
2471 None => {
2472 state.set_top_idx(top - 1);
2473 Ok(false)
2474 }
2475 }
2476}
2477
2478pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2479 let _level = index_to_stack_idx(state, idx);
2480 Ok(())
2483}
2484
2485pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2486 if n > 0 {
2487 state.concat(n)?;
2488 } else {
2489 let empty = state.intern_str(b"")?;
2490 state.push(LuaValue::Str(empty));
2491 }
2492 state.gc_pre_collect_clear();
2493 state.gc().check_step();
2494 Ok(())
2495}
2496
2497pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2498 let t = index_to_value(state, idx);
2499 let result = state.obj_len(&t)?;
2500 state.push(result);
2501 Ok(())
2502}
2503
2504pub fn set_warn_f(state: &mut LuaState, f: Option<Box<dyn FnMut(&[u8], bool)>>) {
2509 state.global_mut().warnf = f;
2511}
2512
2513pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2514 state.emit_warning(msg, tocont);
2515}
2516
2517pub fn new_userdata_uv(
2518 state: &mut LuaState,
2519 size: usize,
2520 nuvalue: i32,
2521) -> Result<GcRef<LuaUserData>, LuaError> {
2522 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2523 let u = state.new_userdata(size, nuvalue as usize)?;
2524 state.push(LuaValue::UserData(u.clone()));
2525 state.gc_pre_collect_clear();
2526 state.gc().check_step();
2527 Ok(u)
2528}
2529
2530fn aux_upvalue(state: &LuaState, fi: &LuaValue, n: i32) -> Option<(Vec<u8>, LuaValue)> {
2536 match fi {
2537 LuaValue::Function(LuaClosure::C(ccl)) => {
2538 let upvalues = ccl.upvalues.borrow();
2539 let nupvalues = upvalues.len() as i32;
2540 if n < 1 || n > nupvalues {
2541 return None;
2542 }
2543 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2544 }
2545 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2546 let nupvalues = lcl.upvals.len() as i32;
2547 if n < 1 || n > nupvalues {
2548 return None;
2549 }
2550 let val = state.upvalue_get(lcl, (n - 1) as usize);
2551 let name: Vec<u8> = lcl
2555 .proto
2556 .upvalues
2557 .get((n - 1) as usize)
2558 .and_then(|ud| ud.name.as_ref())
2559 .map(|s| s.as_bytes().to_vec())
2560 .unwrap_or_else(|| b"(no name)".to_vec());
2561 Some((name, val))
2562 }
2563 _ => None,
2564 }
2565}
2566
2567pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2568 let fi = index_to_value(state, funcindex);
2569 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2570 state.push(val);
2571 Some(name)
2572 } else {
2573 None
2574 }
2575}
2576
2577pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2578 let fi = index_to_value(state, funcindex);
2579 let (name, _) = aux_upvalue(state, &fi, n)?;
2580 let new_val = state.pop();
2581 match &fi {
2582 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2583 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2584 }
2585 LuaValue::Function(LuaClosure::C(ccl)) => {
2586 let idx = (n - 1) as usize;
2587 {
2588 let mut upvalues = ccl.upvalues.borrow_mut();
2589 if idx >= upvalues.len() {
2590 return None;
2591 }
2592 upvalues[idx] = new_val.clone();
2593 }
2594 state.gc().barrier(ccl, &new_val);
2595 }
2596 _ => return None,
2597 }
2598 Some(name)
2599}
2600
2601fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2604 let fi = index_to_value(state, fidx);
2605 debug_assert!(
2606 matches!(fi, LuaValue::Function(LuaClosure::Lua(_))),
2607 "Lua function expected"
2608 );
2609 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2610 let sizeupvalues = lcl.upvals.len() as i32;
2611 if n >= 1 && n <= sizeupvalues {
2612 Some((n - 1) as usize)
2613 } else {
2614 None
2615 }
2616 } else {
2617 None
2618 }
2619}
2620
2621pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2623 let fi = index_to_value(state, fidx);
2624 match &fi {
2625 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2626 let idx = get_upval_ref_idx(state, fidx, n)?;
2627 Some(GcRef::identity(&lcl.upval(idx)))
2629 }
2630 LuaValue::Function(LuaClosure::C(ccl)) => {
2631 let upvalues = ccl.upvalues.borrow();
2632 if n >= 1 && n <= upvalues.len() as i32 {
2633 Some(GcRef::identity(ccl) ^ (n as usize))
2636 } else {
2637 None
2638 }
2639 }
2640 LuaValue::Function(LuaClosure::LightC(_)) => None,
2641 _ => {
2642 debug_assert!(false, "function expected");
2643 None
2644 }
2645 }
2646}
2647
2648pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2650 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2651 Some(i) => i,
2652 None => return,
2653 };
2654 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2655 Some(i) => i,
2656 None => return,
2657 };
2658 let f1 = index_to_value(state, fidx1);
2659 let f2 = index_to_value(state, fidx2);
2660 if let (LuaValue::Function(LuaClosure::Lua(lcl1)), LuaValue::Function(LuaClosure::Lua(lcl2))) =
2661 (&f1, &f2)
2662 {
2663 let shared = lcl2.upval(idx2);
2664 lcl1.set_upval(idx1, shared);
2665 state.gc().obj_barrier(lcl1, &shared);
2666 }
2667}
2668
2669